[atARC110E]Shorten ABC
考虑令$a$、$b$和$c$分别对应1、2和3,那么每一次相当于令$x$和$y$变为$x\oplus y$(要求$x\ne y$)
根据异或的结合律,我们相当于将其划分为若干个区间求异或值
(另外还有$x\ne y$的条件,归纳可证等价于要求区间异或值不为0且区间内字母不完全相等或仅有1个字母)
为了保证每一种方案都不同,强制除了第一个以外的区间任意非空前缀异或值都不为0,否则可以把该前缀加入到上一个区间中(同时此时要保证了$x\ne y$的条件)
令$f_{i}$表示前$i$个字母对应的方案数,考虑递推转移,设$j$为第一个异或前缀和等于$i$的,那么就转移到$(i,j)$,区间修改可以用差分来维护
初始状态需要考虑一下,首先需要保证异或不为0,然后若所有字母完全相同(必然是奇数个),可以理解为最后$len-1$个字母是由下一段一个非空前缀补上来的,因此允许出现
特别的,当所有字母都相同时答案为1,需要特判
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1000005 4 #define mod 1000000007 5 int n,a[N],nex[N],lst[4],f[N]; 6 char s[N]; 7 void update(int l,int r,int x){ 8 f[l]=(f[l]+x)%mod; 9 if (r+1<n)f[r+1]=(f[r+1]+mod-x)%mod; 10 } 11 int main(){ 12 scanf("%d%s",&n,s); 13 for(int i=0;i<n;i++)a[i]=s[i]-'A'+1; 14 bool flag=0; 15 for(int i=1;i<n;i++) 16 if (s[i]!=s[0])flag=1; 17 if (!flag){ 18 printf("1"); 19 return 0; 20 } 21 for(int i=1;i<n;i++)a[i]^=a[i-1]; 22 for(int i=0;i<4;i++)lst[i]=n; 23 for(int i=n-1;i>=0;i--){ 24 nex[i]=lst[a[i]]; 25 lst[a[i]]=i; 26 } 27 for(int i=0;i<n;i++) 28 if (a[i])update(i,i,1); 29 for(int i=0;i<n;i++){ 30 if (i)f[i]=(f[i]+f[i-1])%mod; 31 update(i+1,nex[i]-1,f[i]); 32 } 33 printf("%d",f[n-1]); 34 }