ARC110E - Shorten ABC

一个由\(ABC\)组成的字符串,每次找相邻的两个不同的字符,将它们替换成那个和它们都不想同的字符(如\(AB\to C\))。

问若干次操作能够形成的字符串的方案数。

\(n\le 10^6\)


类似于AGC027E。

然而还是做不出来。

\(A,B,C\)分别为\(1,2,3\)。可以发现每次操作之后异或和不变。

并且可以发现如果出现了\(AA\)的情况,可以将它消掉(除非全部相等)。

于是可以发现一段字符串形成一个字符的充分必要条件是:异或和相等,字符不全相等(长度为\(1\)的除外)。

先把全部相等的特判掉。

对于一个最终形成的字符串,每个字符匹配原串的一个子串。

搞个DP,转移的时候找最小的一段。可以发现找最小的一段可以保证字符不全相等。

统计答案的时候,如果剩下的一段异或和为\(0\),那么就计入答案中。因为异或和为\(0\),肯定有办法将后面这些消掉。

(或者也可以这样:如果剩下一段异或和不为\(0\)就计入答案中。相当于最后一个字符特殊处理。如果这一段全部相等,它实际上可以和前面的互相消。因为特判了全部相同的情况,所以合法的方案总是存在的。)


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1000005
#define mo 1000000007
#define ll long long
int n;
char str[N];
int a[N],s[N];
int lst[N][4];
int f[N];
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d%s",&n,str+1);
	for (int i=1;i<=n;++i){
		a[i]=str[i]-'A'+1;
		s[i]=s[i-1]^a[i];
	}
	bool same=1;
	for (int i=1;i<n;++i)
		same&=(a[i]==a[i+1]);
	if (same || n==1){
		printf("1\n");
		return 0;
	}
	lst[n+1][0]=lst[n+1][1]=lst[n+1][2]=lst[n+1][3]=n+1;
	for (int i=n;i>=0;--i){
		memcpy(lst[i],lst[i+1],sizeof lst[i]);
		lst[i][s[i+1]]=i+1;
	}
	f[0]=1;
	ll ans=0;
	for (int i=0;i<n;++i)
		for (int c=1;c<=3;++c)
			(f[lst[i][c^s[i]]]+=f[i])%=mo;
	for (int i=1;i<=n;++i)
		if ((s[n]^s[i])==0)
			ans+=f[i];
   	/*
    for (int i=0;i<n;++i)
		if (s[n]^s[i])
			ans+=f[i];
	*/
	ans%=mo;
	printf("%lld\n",ans);
	return 0;
}
posted @ 2020-12-18 21:23  jz_597  阅读(283)  评论(0编辑  收藏  举报