首页 > 代码库 > [bzoj3160]万径人踪灭

[bzoj3160]万径人踪灭

Manacher+FFT,这题太精妙了,我现在才写是不是太弱了...

Ans=以某个轴为中心的每一种回文子序列-所有连续回文串方案数

连续部分可以用Manacher在O(n)时间内解决。

第一部分:令f[i]=以i为中轴的对称对数,则对(2^f[i])-1求和即可(不能光有一根轴)

长串中i左右对称的对数位置相加一定是i(在短串中),那么f[i]=[sigma((str[x]==str[i-x])(x<i))+1]/2

这时题目还有个条件哟~串只是由a,b构成的,我们考虑一个卷积的形式C[k]=sigma(a[x]*b[k-x],0<=x<=k),与上面是如出一辙的,只需分别考虑a,b的贡献做两个FFT。

能独立推出来的人都是大大大神....Orz PoPoQQQ!

 

细节还是有点多,特别要注意长串与短串的下标(有没有插特殊符号)。

技术分享
 1 #include<cstdio>
 2 #include<cmath>
 3 #include<complex>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define ll long long
 7 #define N 502020
 8 #define mo 1000000007
 9 using namespace std;
10 typedef complex<double> E;
11 const double pi=acos(-1);
12 int n,m,len,p[N],R[N],L;
13 char str[N];
14 ll ans=0,ans1=0,bin[N];
15 E a[N],f[N],g[N];
16 void manacher(){
17     str[0]=-;str[len+1]=+;
18     int id,mx=0;
19     for(int i=1;i<=len;i++){
20         if(mx>i)p[i]=min(mx-i+1,p[2*id-i]);else p[i]=1;
21         while(str[i+p[i]]==str[i-p[i]])p[i]++;
22         if(i+p[i]-1>mx)mx=i+p[i]-1,id=i;
23         ans1=(ans1+p[i])%mo;
24     }id=0,mx=0;
25     for(int i=1;i<=len;i++){
26         if(mx>i)p[i]=min(mx-i,p[2*id-i]);else p[i]=0;
27         while(str[i+p[i]+1]==str[i-p[i]])p[i]++;
28         if(i+p[i]>mx)mx=i+p[i],id=i;
29         ans1=(ans1+p[i])%mo;
30     }
31 }
32 void fft(E *a,int f){
33     for(int i=1;i<n;i++)if(i>R[i])swap(a[i],a[R[i]]);
34     for(int i=1;i<n;i<<=1){
35         E wn(cos(pi/i),sin(pi/i)*f);
36         for(int j=0;j<n;j+=i<<1){
37             E w(1,0);
38             for(int k=0;k<i;k++,w*=wn){
39                 E x=a[j+k],y=w*a[j+k+i];
40                 a[j+k]=x+y;a[j+k+i]=x-y;
41             }
42         }
43     }
44     if(f==-1)for(int i=0;i<n;i++)a[i]/=n;
45 }
46 int main()
47 {
48     scanf("%s",str+1);len=strlen(str+1);
49     bin[0]=1;for(int i=1;i<=100000;i++)bin[i]=bin[i-1]*2%mo;
50     manacher();
51     m=2*(len-1);for(n=1;n<=m;n*=2)L++;
52     for(int i=1;i<n;i++)R[i]=(R[i/2]/2)|((i&1)<<(L-1));
53     for(int i=0;i<len;i++)a[i]=(str[i+1]==a);
54     fft(a,1);for(int i=0;i<n;i++)f[i]=a[i]*a[i];fft(f,-1);
55     memset(a,0,sizeof(a));
56     for(int i=0;i<len;i++)a[i]=(str[i+1]==b);
57     fft(a,1);for(int i=0;i<n;i++)g[i]=a[i]*a[i];fft(g,-1);
58     for(int i=0;i<n;i++){
59         int x=f[i].real()+g[i].real()+0.1;
60         ans=(ans+bin[(x+1)/2])%mo;
61         ans=(ans-1+mo)%mo;
62     }    
63     //printf("%lld %lld\n",ans,ans1);
64     printf("%lld\n",(ans-ans1+mo)%mo);
65     return 0;
66 }
代码还是看hzwer的好

 

最后想说一下n与m的关系,假设要求0...n,m=2×n之后,n变成了大于m的2的幂次。

但是最后答案是在0...n×2中的。

像2194那个题,题面给出的信息经转化之后便可看成两个多项式相乘,对应的下标就是对应的次数。都是有道理的。

= =Manacher不会背

 

[bzoj3160]万径人踪灭