BZOJ3160:万径人踪灭

Description

Input & Output & Sample Input & Sample Output

HINT

 

题解:

题意即求不连续但间隔长度对称的回文串个数。

若si=sj,则这对字符可以作为以(i+j)/2为中心的回文串的一部分

用F[i]来表示可以做为以i/2为中心的回文串的一部分的字符对数,则以i/2为中心的回文串数为2^F[i]

则这就成了多项式乘法:先做一次a的,把字符为a的位置值赋为1,其余为0,进行一次FFT;同理做一次b的。

因为完全连续是不可以的,所以用Manacher求出这样的回文串的个数并减去。

 

代码:

(BZOJ上PASCAL跑得不够快,再加上这题时限只有10s,并没有AC,不知道那些用PASCAL通过的人是怎么卡常数的)

 1 uses math;
 2 const
 3   mo=1000000007;
 4 type
 5   xs=record
 6     x,y:double;
 7   end;
 8   arr=array[0..270001]of xs;
 9 var
10   e,t:arr;
11   a:array[1..5]of arr;
12   n,m,i,ll,ans:longint;
13   ch:array[-2..270001]of char;
14   b:array[-2..270001]of longint;
15   z,ksm:array[0..270001]of longint;
16   s:ansistring;
17 operator -(a,b:xs)c:xs;
18 begin c.x:=a.x-b.x; c.y:=a.y-b.y; end;
19 operator +(a,b:xs)c:xs;
20 begin c.x:=a.x+b.x; c.y:=a.y+b.y; end;
21 operator *(a,b:xs)c:xs;
22 begin c.x:=a.x*b.x-a.y*b.y; c.y:=a.x*b.y+a.y*b.x; end;
23 procedure manacher;
24 var k,l,i:longint;
25 begin
26   k:=-1; l:=-1; b[-1]:=1;
27   for i:=0 to m*2-1 do
28   begin
29     if l>=i then
30     b[i]:=min(b[2*k-i],l-i+1)else b[i]:=1;
31     while true do
32     begin
33       if ch[i+b[i]]=ch[i-b[i]] then inc(b[i])
34       else break;
35     end;
36     ans:=(ans+mo-(b[i]shr 1))mod mo;
37     if i+b[i]-1>l then begin l:=i+b[i]-1; k:=i; end;
38   end;
39 end;
40 procedure fft(xx:longint);
41 var i,j,q,k,l,c:longint;
42   t:xs;
43 begin
44   for i:=0 to n-1 do a[xx+2,z[i]]:=a[xx,i];
45   xx:=xx+2;
46   k:=n; l:=1;
47   for i:=ll downto 1 do
48   begin
49     k:=k shr 1;
50     for j:=0 to k-1 do
51     begin
52       c:=j*2*l;
53       for q:=0 to l-1 do
54       begin
55         t:=e[q*k]*a[xx,c+l];
56         a[xx,c+l]:=a[xx,c]-t;
57         a[xx,c]:=a[xx,c]+t;
58         inc(c);
59       end;
60     end;
61     l:=l*2;
62   end;
63 end;
64 begin
65   readln(s); m:=length(s);
66   ch[-2]:='+';
67   for i:=0 to m-1 do ch[i*2]:=s[i+1];
68   ch[m*2+1]:='-';
69   manacher;
70   for i:=0 to m-1 do if ch[i*2]='a' then a[1,i].x:=1;
71   for i:=0 to m-1 do if ch[i*2]='b' then a[2,i].x:=1;
72   n:=1;
73   while n<m*2 do begin n:=n*2; inc(ll); end;
74   for i:=1 to n-1 do z[i]:=(z[i shr 1]shr 1)or((i and 1)shl(ll-1));
75   ksm[0]:=1; for i:=1 to 100000 do ksm[i]:=(ksm[i-1]*2)mod mo;
76   for i:=0 to n-1 do e[i].x:=cos(pi*2*i/n);
77   for i:=0 to n-1 do e[i].y:=sin(pi*2*i/n);
78   fft(1); fft(2);
79   for i:=0 to n-1 do a[3,i]:=a[3,i]*a[3,i]+a[4,i]*a[4,i];
80   for i:=0 to n-1 do e[i].y:=-e[i].y;
81   fft(3);
82   for i:=0 to m-1 do a[5,i*2].x:=a[5,i*2].x+n;
83   for i:=0 to n-1 do ans:=(ans+ksm[round(a[5,i].x/2/n)]-1)mod mo;
84   writeln(ans);
85 end.
PASCAL
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int mo=1000000007;
 4 typedef pair<double,double> pa;
 5 pa operator + (pa a,pa b)
 6 { pa c; c.first=a.first+b.first; c.second=a.second+b.second; return c; }
 7 pa operator - (pa a,pa b)
 8 { pa c; c.first=a.first-b.first; c.second=a.second-b.second; return c; }
 9 pa operator * (pa a,pa b)
10 { pa c; c.first=a.first*b.first-a.second*b.second; c.second=a.first*b.second+a.second*b.first; return c; }
11 pa a[5][270005],e[270005];
12 char s[270005],s2[270005];
13 int n,ans,nn,m,ksm[270005],z[270005];
14 void manacher()
15 {
16     int l=1,k=1; z[1]=0;
17     for(int i=2;i<=nn;i++)
18     {
19         if(l>=i)z[i]=min(z[2*k-i],l-i);else z[i]=0;
20         while(s2[i+z[i]+1]==s2[i-z[i]-1])z[i]++;
21         ans=(ans-(z[i]+1)/2)%mo; if(i+z[i]>l){ k=i; l=i+z[i]; }
22     }
23 }
24 void fft(int x)
25 {
26     for(int i=0;i<m;i++)a[x+1][z[i]]=a[x][i]; x++;
27     for(int k=m/2,i=1;i<m;i*=2,k/=2) for(int j=0;j<m;j+=2*i) for(int l=0;l<i;l++)
28     { pa t=e[k*l]*a[x][j+l+i]; a[x][j+l+i]=a[x][j+l]-t; a[x][j+l]=a[x][j+l]+t; }
29 }
30 int main()
31 {
32     scanf("%s",s+1); n=strlen(s+1); nn=1; s2[0]='!'; s2[1]='*';
33     for(int i=1;i<=n;i++){ nn++; s2[nn]=s[i]; nn++; s2[nn]='*'; } s2[nn+1]='?';
34     manacher();
35     ksm[0]=1; for(int i=1;i<=270000;i++)ksm[i]=ksm[i-1]*2%mo;
36     m=1; while(m<2*n)m=m*2;
37     for(int i=1;i<m;i++)z[i]=(z[i>>1]>>1)+(i&1)*m/2;
38     e[0].first=1; e[1].first=cos(2*acos(-1)/m); e[1].second=sin(2*acos(-1)/m);
39     for(int i=2;i<m;i++)e[i]=e[i-1]*e[1];
40     for(int i=1;i<=n;i++)if(s[i]=='a')a[0][i-1].first=1; fft(0);
41     for(int i=1;i<=n;i++)if(s[i]=='b')a[2][i-1].first=1; fft(2);
42     for(int i=0;i<m;i++)a[3][i]=a[1][i]*a[1][i]+a[3][i]*a[3][i];
43     for(int i=0;i<m;i++)e[i].second=-e[i].second;
44     fft(3);
45     for(int i=0;i<m;i++)ans=(ans+ksm[((int)round(a[4][i].first/m)+1)/2]-1)%mo;
46     ans=(ans+mo)%mo;
47     printf("%d\n",ans);
48 }
C++

转载于:https://www.cnblogs.com/GhostReach/p/6257568.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值