1. 程式人生 > >【BZOJ3160】萬徑人蹤滅 Manacher+FFT

【BZOJ3160】萬徑人蹤滅 Manacher+FFT

補集 double highlight 角度 貢獻 tdi 得到 oid iostream

【BZOJ3160】萬徑人蹤滅

Description

技術分享

技術分享

Input

技術分享

Output

Sample Input

Sample Output

HINT

技術分享

題解:自己想出來1A,先撒花~(其實FFT部分挺裸的)

做這道題,第一思路很重要,顯然看到這題的第一想法就是ans=總數-不合法(不要問我為什麽顯然)。因為向這種用補集法的題一般都會給一些很奇葩的限制條件,但是一旦換個角度去想就很水了,好了不多說廢話了。

顯然,不合法的情況,也就是連續的回文區間的方案數,我們直接上Manacher就搞定了嘛!答案就是所有對稱軸的(最長回文串長度+1)/2之和(是的,很顯然)

對於不合法的情況,我們發現兩串對稱的情況跟卷積的形式類似(FFT做多了吧?),但是問題來了,怎麽構造出一個卷積,使得它的值就是回文子串的個數呢?

我們發現原串只有a或b,所以思考能不能構造出一種卷積,使得對應位置的值跟下面一樣

a*b->0 b*a->0 a*a->1 b*b->1

如果把a看成0,b看成1,這顯然是一個異或,然而並沒什麽卵用。但我們如果把a看成0,b看成1,可以滿足只有b*b是1,其他都是0;同理,把a看成1,b看成0,可以滿足只有a*a是1,其他都是0,然後我們對這兩種情況分別求一次卷積,就能得到:以i為對稱中心的最長子串的回文半徑長度。這裏註意一下,用於是兩個一樣的多項式相乘,所以每對字符會被算成兩次(單個字符自我對稱的除外),所以我們要的回文半徑應該是(x+1)/2

然而半徑長度並不是方案數,由於每對對稱的字符都可以選或不選,所以對答案的貢獻就是2^長度-1(因為你不能一個也不選吧?)

好吧,感覺說了這麽多又有點說不明白了,所以歡迎提問和hack~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#define pi acos(-1.0)
#define mod 1000000007
using namespace std;
int n,ans;
struct cp
{
	double x,y;
	cp (double a,double b){x=a,y=b;}
	cp (){}
	cp operator + (cp a){return cp(x+a.x,y+a.y);}
	cp operator - (cp a){return cp(x-a.x,y-a.y);}
	cp operator * (cp a){return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
}n1[1<<20],n2[1<<20];
int s[1<<20],ret[1<<20],rl[1<<20],pn[1<<20];
char str[1<<20];
void FFT(cp *a,int len,int f)
{
	int i,j,k,h;
	cp t;
	for(i=k=0;i<len;i++)
	{
		if(i>k)	swap(a[i],a[k]);
		for(j=(len>>1);(k^=j)<j;j>>=1);
	}
	for(h=2;h<=len;h<<=1)
	{
		cp wn(cos(f*2*pi/h),sin(f*2*pi/h));
		for(j=0;j<len;j+=h)
		{
			cp w(1.0,0);
			for(k=j;k<j+h/2;k++)	t=w*a[k+h/2],a[k+h/2]=a[k]-t,a[k]=a[k]+t,w=w*wn;
		}
	}
}
void work(cp *a,cp *b,int len)
{
	FFT(a,len,1),FFT(b,len,1);
	for(int i=0;i<len;i++)	a[i]=a[i]*b[i];
	FFT(a,len,-1);
	for(int i=0;i<len;i++)	ret[i]+=(int)(a[i].x/len+0.1);
}
int main()
{
	scanf("%s",str);
	int i,mx,pos,len=strlen(str);
	for(i=0;i<len;i++)	s[n++]=0,s[n++]=str[i]-‘a‘;
	s[n++]=0;
	for(mx=-1,i=0;i<n;i++)
	{
		if(mx>i)	rl[i]=min(mx-i+1,rl[2*pos-i]);
		else	rl[i]=1;
		for(;i+rl[i]<n&&rl[i]<=i&&s[i+rl[i]]==s[i-rl[i]];rl[i]++);
		if(mx<i+rl[i]-1)	mx=i+rl[i]-1,pos=i;
	}
	for(i=0;i<n;i++)	ans=(ans-rl[i]/2+mod)%mod;
	for(len=1;len<2*n;len<<=1);
	for(i=0;i<len;i++)	n1[i]=n2[i]=cp(0,0);
	for(i=1;i<n;i+=2)	n1[i]=n2[i]=cp(s[i],0);
	work(n1,n2,len);
	for(i=0;i<len;i++)	n1[i]=n2[i]=cp(0,0);
	for(i=1;i<n;i+=2)	n1[i]=n2[i]=cp(s[i]^1,0);
	work(n1,n2,len);
	for(pn[0]=i=1;i<=n;i++)	pn[i]=(pn[i-1]<<1)%mod;
	for(i=0;i<n;i++)	ans=(ans+pn[(ret[i<<1]+(i&1))>>1]-1)%mod;
	printf("%d",ans);
	return 0;
}

【BZOJ3160】萬徑人蹤滅 Manacher+FFT