2013湖北互测week1
吐槽:我替“千山鸟飞绝”感到不服。
manacher+FFT,思路好题
补集思想,直接求不连续的有点难,我们可以求出所有回文串,然后减去其中连续的。
连续回文串很好求,manacher就可以解决。
问题在于如何求不一定连续的回文串,如果求出以每个位置i为对称中心,对称的字符对的个数f[i],那对于答案的贡献就加上2^f[i]-1,证明很显然。
于是问题又成了如何求f[i]。
发现字符串中只有两种字符,而且对称的点的下标和为定值。于是我们可以先令a=1、b=0,再令a=0、b=1,分别用FFT求出卷积,就可以得到相等且下标和为x的字符对数。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 300005 #define mod 1000000007 using namespace std; int n,len,ans,rev[maxn],f[maxn],bin[maxn]; char s[maxn],st[maxn]; const double pi=acos(-1.0); struct cp { double x,y; cp(double xx=0,double yy=0){x=xx;y=yy;} friend cp operator +(cp a,cp b){return cp(a.x+b.x,a.y+b.y);} friend cp operator -(cp a,cp b){return cp(a.x-b.x,a.y-b.y);} friend cp operator *(cp a,cp b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);} }a[maxn],b[maxn]; void fft(cp *x,int n,int flag) { F(i,0,n-1) if (rev[i]>i) swap(x[i],x[rev[i]]); for(int m=2;m<=n;m<<=1) { int mid=m>>1; cp wn(cos(2.0*pi/m*flag),sin(2.0*pi/m*flag)); for(int i=0;i<n;i+=m) { cp w(1.0,0); F(j,0,mid-1) { cp u=x[i+j],v=x[i+j+mid]*w; x[i+j]=u+v;x[i+j+mid]=u-v; w=w*wn; } } } if (flag==-1) F(i,0,n-1) x[i].x/=n; } void manacher() { int mx=0,id=0; F(i,1,n) { if (mx>i) f[i]=min(mx-i,f[id*2-i]); else f[i]=0; while (s[i+f[i]]==s[i-f[i]]) f[i]++; if (i+f[i]>mx) mx=i+f[i],id=i; ans=(ans-f[i]/2+mod)%mod; } } int main() { bin[0]=1;F(i,1,100000) bin[i]=bin[i-1]*2%mod; scanf("%s",s+1); len=strlen(s+1); n=1;int tmp=0; while (n<len*2) n<<=1,tmp++; F(i,1,n-1){rev[i]=rev[i>>1]>>1|((i&1)<<(tmp-1));} F(i,0,len-1) if (s[i+1]=='a') a[i].x=1; fft(a,n,1); F(i,0,n-1) a[i]=a[i]*a[i]; fft(a,n,-1); F(i,0,len-1) if (s[i+1]=='b') b[i].x=1; fft(b,n,1); F(i,0,n-1) b[i]=b[i]*b[i]; fft(b,n,-1); F(i,0,n-1) { int x=round(a[i].x+b[i].x); x=(x+1)/2; ans=(ans+bin[x]-1)%mod; } F(i,1,len) st[i]=s[i]; n=len<<1|1; s[0]='*';s[1]='#';s[n+1]='&'; F(i,1,len) s[i<<1]=st[i],s[i<<1|1]='#'; manacher(); printf("%d\n",ans); return 0; }