AT4514 [AGC030E] Less than 3 题解

一道感觉挺神的题目。


考虑在操作中寻找一些方便计算的东西用来转化。

假设在对第 (i(i eq 1,i eq n)) 个位置操作,那么该操作合法的一个必要条件是 (s_{i-1} eq s_{i+1}) ,那么该串中同色的连续段个数不会变。

考虑对于每一个 (s_i eq s_{i+1}) 的位置做一个标记,且若 (s_i=0) ,标记为 (0) ,若 (s_i=1) ,标记为 (1)
可以发现:

  • 把标记连在一起之后,必定是 (0,1) 交替的样子
  • 一次操作就是将一个标记左移或右移一位
  • 最后的目标就是将所有标记对齐
  • 一个状态合法,当且仅当任意两个标记距离小于等于 (2) ,且中间位置的标记互不重合

但这有一个问题,没办法处理两端的位置。
考虑在两端继续补充无穷多的标记,且这些标记满足 (0,1) 交替,那么可以通过中间的标记和两端的标记互相转移,处理两端的位置的修改操作。

最后考虑对于两个标记的序列,如何计算答案。

  • 一个结论是直接计算对应标记的距离和。

虽然这个结论没有考虑序列的合法性,但是这个结论是正确的。考虑改变移动标记的顺序,可以感性地理解。

但是由于两端标记的存在,必须用 (O(n)) 的时间枚举两端标记的个数,然后一次答案的计算是 (O(n)) 的。

所以得到了总复杂度为 (O(n^2)) 的做法。

题外话:Atcoder 里思维题还是挺好的(思维难度大,代码难度小)。

Code:(感觉不是很好看

#define N 20005
char s[N],t[N];
int a[N],b[N],ta[N],tb[N];
int c1,c2,n;
signed main()
{
	cin>>n;
	scanf("%s%s",s+1,t+1);
	if(s[1]=='1') ta[++c1]=0;
	for(int i=1;i<n;i++) if(s[i]!=s[i+1]) ta[++c1]=i;
	if(s[n]=='1') ta[++c1]=n;
	if(t[1]=='1') tb[++c2]=0;
	for(int i=1;i<n;i++) if(t[i]!=t[i+1]) tb[++c2]=i;
	if(t[n]=='1') tb[++c2]=n;
	for(int i=1;i<=c1;i++) a[i+c2]=ta[i];
	for(int i=c1+c2+1;i<=c1+c2+c2;i++) a[i]=n;
	int ans=1e9;
	for(int i=0;i<=c2+c1;i+=2)
	{
		for(int j=1;j<=i;j++) b[j]=0;
		for(int j=1;j<=c2;j++) b[i+j]=tb[j];
		for(int j=i+c2+1;j<=c1+c2+c2;j++) b[j]=n;
		int R=0;
		for(int j=1;j<=c1+c2+c2;j++) R+=abs(a[j]-b[j]);
		if(R<ans) ans=R;
	}
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/wasa855/p/12465146.html