您的位置:首页 > 其它

bzoj3160 万径人踪灭

2017-08-31 21:31 190 查看
传送门

奇怪的FFT+manacher以及容斥(?)。

题目要求求出不连续的回文序列,首先要想到用所有回文序列减掉连续的回文序列,连续的显然可以用manacher求出来,于是题目转化为求出所有的回文序列。巨大的脑洞:由于题目所给的字符串只包含a和b两种字符,所以我们将a看成1,b看成0做一遍FFT,然后将b看成1,a看成0再做一遍,就可以求出所有的对称字符对的对称中心下标乘2包含的对称字符对(大概理解一下)。这中间有个问题:对称中心有可能在字符上,也有可能在两个字符之间。这在FFT的作用下导致了一个问题:对于两个下标不同的字符对,它们会对其对称中心下标乘2的位置带来2的贡献;而对于同一个字符,它会给自己带来1的贡献。对于这个问题的处理,网上大多数的题解都是将贡献除以2然后上取整,于是导致我懵逼了一天,最后在zyz神犇的引导下解开了谜团;谜团的本质在之前已经说了,就不再详细赘述做法了。其余见代码。

CODE:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<complex>
#include<iostream>
using namespace std;
#define mod 1000000007ll
typedef long long ll;
typedef complex<double> cp;
const double pi=acos(-1);
const int N=3e5+10;
cp a
,b
;
char s
;
int r
,Ans
,p
,power
;
int len,n,m,Len;
ll ans;
inline int getstring()
{
char c=getchar();int len=0;
while(c!='a'&&c!='b') c=getchar();
while(c=='a'||c=='b') s[++len]=c,c=getchar();
return len;
}
inline void fft(cp *a,int f)
{
for(int i=0;i<n;i++)
if(i<r[i]) swap(a[i],a[r[i]]);
for(int i=1;i<n;i<<=1)
{
cp wn(cos(pi/i),f*sin(pi/i));
for(int j=0,tmp=i<<1;j<n;j+=tmp)
{
cp w(1,0);
for(int k=0;k<i;k++,w*=wn)
{
cp x=a[j+k],y=w*a[j+k+i];
a[j+k]=x+y,a[j+k+i]=x-y;
}
}
}
}
inline ll manacher()
{
int mx=0,pos=0;
ll ans=0;
for(int i=len;i;i--)
s[i<<1]=s[i],s[(i<<1)-1]='#';
len<<=1;
s[len+1]=s[len+2]='#',s[0]='$';
for(int i=1;i<=len;i++)
{
if(mx>i) p[i]=min(p[pos*2-i],mx-i);
else p[i]=1;
while(s[i-p[i]]==s[i+p[i]]) p[i]++;
if(i+p[i]>mx) mx=i+p[i],pos=i;
ans+=1ll*p[i]>>1;
}
return ans;
}
int main()
{
len=getstring();m=len<<1;
for(n=1;n<m;n<<=1) Len++;
for(int i=0;i<n;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(Len-1));
for(int i=1;i<=len;i++)
if(s[i]=='a') a[i-1]=1;
else b[i-1]=1;
fft(a,1),fft(b,1);
for(int i=0;i<n;i++)
a[i]*=a[i],b[i]*=b[i];
fft(a,-1),fft(b,-1);
power[0]=1;
for(int i=1;i<=m;i++)
{
power[i]=power[i-1]<<1;
if(power[i]>=mod) power[i]-=mod;
}
for(int i=0;i<m;i++)
{
int tmp=(int)(a[i].real()/n+0.5)+(int)(b[i].real()/n+0.5)+1;
ans+=power[tmp>>1]-1;
if(ans>=mod) ans-=mod;
}
/*和上一个for的本质相同
for(int i=0;i<m;i++)
{
if(i&1)
{
int tmp=(int)(a[i].real()/n+0.5)+(int)(b[i].real()/n+0.5);
ans+=power[tmp>>1]-1;
}
else
{
int tmp=(int)(a[i].real()/n+0.5)+(int)(b[i].real()/n+0.5);
ans+=power[(tmp>>1)+1]-1;
}
if(ans>=mod) ans-=mod;
}
*/
printf("%lld",((ans-manacher())%mod+mod)%mod);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  FFT manacher 容斥 bzoj