3160: 万径人踪灭
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 2360 Solved: 1274
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
Sample Output
HINT
Source
2013湖北互测week1
题意见题面,不想再多叙述了,应该不是一道难题,但是菜鸡写了很久。感觉自己最近码力不够了,需要多加锻炼呀!
第一个想法很简单,把那些连续的段筛出,也就是用Manacher求个回文串个数。不连续的段呢?对于每个对称轴,我们把原字符串和按轴反转后的字符串进行比较,统计相同的字符对(这些对就是之前关于于对称轴的相同对)CNT,考虑每个对的选与不选,然后答案就是Σ((1<<CNT)-1),减去1是因为空集显然不是答案。
那么,每次我们不断移动串s和串的反转rev,s与rev之间的相对位置不同就等价于对称轴的不同。另外由于只有a,b两种字符,可以用01来表示,用两个串的同或的bitcount来求出CNT。最后用bitset压缩位数,空间和效率就能快32倍了。
bitset是一个很有用的容器,以后要多加掌握。测试过,速度远比暴力快。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#define mo 1000000007
using namespace std;
char s[100005],tmp[200005];
int Len[200005],n,tn,pw[100005],ans;
bitset<100005> B,C,D;
int Mana_Init(char *s, int n)
{
int i;
tmp[0]='@';
tmp[2*n+2]='$';
tmp[2*n+1]='#';
for(int i=1;i<=n*2;i+=2)
tmp[i]='#',tmp[i+1]=s[i>>1];
return 2*n+1;
}
int Manacher(char *s, int n)
{
int mx=0,cnt=0,pos=0;
for(int i=1;i<=n;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*pos-i]);
else
Len[i]=1;
while(s[i-Len[i]]==s[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)
{
mx=Len[i]+i;
pos=i;
}
cnt=(cnt+Len[i]/2)%mo;
}
return cnt;
}
int main()
{
scanf("%s",&s);
n=strlen(s);
pw[0]=1;
for(int i=1;i<=n;i++)
pw[i]=pw[i-1]*2%mo;
for(int i=0;i<n;i++)
B[i]=s[i]=='a',C[n-i-1]=~B[i]; //C储存反串的反,为了把同或变成异或
ans=pw[(B^C).count()+1>>1]-1;
D=C;
for(int i=1;i<n;i++) //左移方向
{
C[n-i]=0; //高位变成零,因为两头是不参与运算的
ans=(ans+pw[(B>>i^C).count()+1>>1]-1)%mo;
}
for(int i=1;i<n;i++) //右移方向
{
B[n-i]=0;
ans=(ans+pw[(B^D>>i).count()+1>>1]-1)%mo;
}
tn=Mana_Init(s,n);
printf("%d",(ans-Manacher(tmp,tn)+mo)%mo);
return 0;
}
然而这份代码没有过(卡时间卡得很紧呀,都不知道有几个测试点)
后来查阅了下题解,是用的FFT,并不是暴力。其实也很好理解。两个字符关于一个轴对称,那么它们的位置总和必然是相同的。这和两个二进制来做一个高精度乘法(无进位)很相似。我们先让a代表1,b代表0做一遍乘法,再让a代表0,b代表1做一遍乘法,最后把相同位置的答案相加,得到的其实就是之前的CNT。
FFT很生疏,当个模板题好了。这个模板用了库中的complex类,所以效率不是很高,稍微改成自己的应该会快很多。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<complex>
#define mo 1000000007
using namespace std;
typedef complex<double> cp;
const double PI=3.14159265358979323846;
char s[100005],tmp[200005];
int Len[200005],n,tn,pw[100005],op[1<<18],rev[1<<18],ans,L,bit;
cp a[1<<18],b[1<<18];
void get_rev(int bit)
{
for(int i=0;i<(1<<bit);i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<bit-1);
}
void FFT(cp *a, int n, int dft)
{
//++a;//if start from 1
cp x,y;
for(int i=0;i<n;i++)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int stp=1;stp<n;stp<<=1)
{
cp wn=exp(cp(0,dft*PI/stp));
for(int j=0;j<n;j+=stp<<1)
{
cp wnk(1,0);
for(int k=j;k<j+stp;k++)
{
x=a[k],y=wnk*a[k+stp];
a[k]=x+y;
a[k+stp]=x-y;
wnk*=wn;
}
}
}
if(dft==-1)
for(int i=0;i<n;i++)
a[i]/=n;
}
int Mana_Init(char *s, int n)
{
int i;
tmp[0]='@';
tmp[2*n+2]='$';
tmp[2*n+1]='#';
for(int i=1;i<=n*2;i+=2)
tmp[i]='#',tmp[i+1]=s[i>>1];
return 2*n+1;
}
int Manacher(char *s, int n)
{
int mx=0,cnt=0,pos=0;
for(int i=1;i<=n;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*pos-i]);
else
Len[i]=1;
while(s[i-Len[i]]==s[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)
{
mx=Len[i]+i;
pos=i;
}
cnt=(cnt+Len[i]/2)%mo;
}
return cnt;
}
int main()
{
scanf("%s",&s);
n=strlen(s);
pw[0]=1;
for(int i=1;i<=n;i++)
pw[i]=pw[i-1]*2%mo;
L=2;
for(bit=1;(1<<bit)<2*n-1;bit++)
L<<=1;
get_rev(bit);
for(int i=0;i<n;i++)
a[i]=s[i]=='a';
for(int i=0;i<n;i++)
b[i]=s[i]=='a';
FFT(a,L,1);
FFT(b,L,1);
for(int i=0;i<L;i++)
a[i]*=b[i];
FFT(a,L,-1);
for(int i=0;i<L;i++)
op[i]=a[i].real()+0.5;
for(int i=n;i<L;i++)
a[i]=b[i]=0;
for(int i=0;i<n;i++)
a[i]=s[i]=='b';
for(int i=0;i<n;i++)
b[i]=s[i]=='b';
FFT(a,L,1);
FFT(b,L,1);
for(int i=0;i<L;i++)
a[i]*=b[i];
FFT(a,L,-1);
for(int i=0;i<L;i++)
op[i]+=a[i].real()+0.5;
for(int i=0;i<L;i++)
ans=(ans+pw[op[i]+1>>1]-1)%mo;
tn=Mana_Init(s,n);
printf("%d",(ans-Manacher(tmp,tn)+mo)%mo);
return 0;
}
最后吐槽一句bzoj居然不支持c++11…太落后了吧……