嘘~ 正在从服务器偷取页面 . . .

洛谷P2882 [USACO07MAR]Face The Right Way G 题解


洛谷P2882 [USACO07MAR]Face The Right Way G 题解

题目链接:P2882 [USACO07MAR]Face The Right Way G

题意: 固定长度转向,求最小次数和相应的长度

(注:下文中的转向操作均以翻转一词替代 qwq)

首先可以想到搜索,状态数 $2^n$ ,直接起飞

那么我们来分析一下,可以看出对于一个位置,如果它被翻转了大于 $2$ 次,那这个一定不是必要的操作

也就是说,一个位置只可能被翻转至多 $2$ 次

而且通过观察样例,我们可以发现其实翻转的顺序和答案无关

很容易想到我们要枚举 $k$ (转向的区间长度),然后进行检查

怎么检查呢?我们要从左开始扫一遍,则至多翻转 $n-k+1$ 次,而每次翻转要 $k$ 个位置

时间复杂度$O(n^3)$ ,显然 $n\le 5000$ 是过不了的

考虑优化翻转操作

为了方便起见,我们可以把 B的位置记为 1,表示要翻转这个位置

记 $f[i]$ 表示 $[i,i+k-1]$ 是否被翻转

那么如果从左往右扫到一个位置 $i$ ,怎么判断这个位置要不要再翻转呢?

我们可以发现,如果 $\sum_{j=i-k+1}^{i-1}f[j]$ 为奇数则该点需要翻转(这个东西自己想一想就知道了

那么我们怎么把这个东西求出来呢?

我们看下一个位置 $i+1$ ,它要求的就是 $\sum_{j=i-k+2}^{i}f[j]$

我们来拆一下可以得到 $\sum_{j=i-k+2}^{i}f[j] = \sum_{j=i-k+1}^{i-1}f[j]+f[i-1]-f[i-k+1]$

有没有发现它可以从 $i$ 的位置转移过来?

那么我们就可以 $O(1)$ 求解这个东西了

来算一下时间复杂度

枚举 $k$ ,从左往右扫一遍,时间复杂度 $O(n^2)$

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define R register
#define MAXN (int)(5e3+5)
int n,m,k;
int d[MAXN],f[MAXN];
int check(R int K)
{
	memset(f,0,sizeof(f)); // 多测(枚举了k)不清空,爆零两行泪 qwq
	R int M=0,sum=0; // sum就是那个f[j]的和
	for(R int i=1; i+K-1<=n; i++)
	{
		if((d[i]+sum)&1)
			f[i]=1,++M; // 要翻转
		sum+=f[i]; // 不要忘了加上
		if(i-K+1>=1)sum-=f[i-K+1]; // 数组不要越界了
	}
	for(R int i=n-K+2; i<=n; i++) // 检查一下翻的对不对,因为这里一段没法翻了
	{
		if((d[i]+sum)&1) return -1145141919810; // 有没翻成功的则无解,返回负无穷 qwq
		if(i-K+1>=1)sum-=f[i-K+1]; // 不要忘了减
	}
	return M;
}
signed main()
{
	ios::sync_with_stdio(0); // 卡常
	cin >> n;
	for(R int i=1; i<=n; i++)
	{
		char ch; cin >> ch;
		d[i]=ch=='B'?1:0; // 表示方向
	}
	m=n,k=1;
	for(R int i=1; i<=n; i++)
	{
		int j=check(i); // 枚举k并算出m
		if(j>=0&&m>j)
			m=j,k=i;
	}
	cout << k << " " << m << endl;
	return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录