[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 } 
View Code

 

posted @ 2020-12-10 09:08  PYWBKTDA  阅读(274)  评论(0编辑  收藏  举报