[BZOJ3160]万径人踪灭(FFT+manacher)

题目描述

传送门

题目大意:在一个只含ab的字符串中选取一个子序列,使得:1、字符和下标都关于一个中心对称2、不能是连续的一段。求方案数。

题解

这题我的方法好蠢啊→_→
首先容斥一下,答案=所有子序列的方案数-回文子串的数量
由于回文的连续子序列一定满足下标对称,所以可以直接用manacher求出回文子串的数量
然后就是统计所有子序列的方案数的问题了
将a和b分开考虑,f(i)和g(i)表示当前这一位是否是当前这个字符,非1即0。然后还是像manacher一样在两个字符串中插入分割符的话,对于一个回文中心i,令F(2i)表示回文中心为i的最长回文子序列长度,那么 F(2i)=jf(j)g(2ij) ,是一个卷积的形式,可以两遍FFT求出然后求和
求出以每一个点为回文中心的最长回文子序列长度了之后F(2i),就是计算方案数的问题,容易知道每一个回文中心的贡献是 2F(2i)1

但是这个方法确实蠢极了!!实际上,满足回文的子序列一定满足对应的元素下标之和为一个定值,无论长度为奇数还是偶数!所以直接一个最简单的式子 F(i)=jf(ij)g(j) 就可以解决了。。这里的F(i)的含义应该是对应元素下标之和为i的回文子序列最长长度

代码

#include
#include
#include
#include
#include
using namespace std;
#define N 1000005
#define Mod 1000000007

const double pi=acos(-1.0);
int lss,ls,p[N],mi[N],F[N],n,m,L,R[N],ans;
char s[N],ss[N];
struct complex
{
    double x,y;
    complex(double X=0,double Y=0)
    {
        x=X,y=Y;
    }
}a[N],b[N];
complex operator + (complex a,complex b) {return complex(a.x+b.x,a.y+b.y);}
complex operator - (complex a,complex b) {return complex(a.x-b.x,a.y-b.y);}
complex operator * (complex a,complex b) {return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}

int manacher()
{
    int id=0,mx=0;
    for (int i=1;iif (mx>i) p[i]=min(p[2*id-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];
            id=i;
        }
    }
    int ans=0;
    for (int i=1;i<=ls;++i)
    {
        if (p[i]==1) continue;
        ans+=(p[i]-2)/2+1;
        ans%=Mod;
    }
    return ans;
}
void FFT(complex a[N],int opt)
{
    for (int i=0;iif (ifor (int k=1;k1)
    {
        complex wn=complex(cos(pi/k),opt*sin(pi/k));
        for (int i=0;i1))
        {
            complex w=complex(1,0);
            for (int j=0;jcomplex x=a[i+j],y=w*a[i+j+k];
                a[i+j]=x+y,a[i+j+k]=x-y;
            }
        }
    }
}
void calc()
{
    FFT(a,1);FFT(b,1);
    for (int i=0;i<=n;++i) a[i]=a[i]*b[i];
    FFT(a,-1);
    for (int i=0;i<=n;++i)
        F[i]+=(int)(a[i].x/n+0.5);
}
int main()
{
    scanf("%s",ss);lss=strlen(ss);s[ls++]='*';
    for (int i=0;i'#',s[ls++]=ss[i];
    s[ls++]='#';
    ans-=manacher();

    m=ls+ls-2;
    for (n=1;n<=m;n<<=1) ++L;
    for (int i=0;i>1]>>1)|((i&1)<<(L-1));

    for (int i=0;i<=n;++i) a[i]=complex(0,0),b[i]=complex(0,0);
    for (int i=0;i'a')?1:0;
    calc();

    for (int i=0;i<=n;++i) a[i]=complex(0,0),b[i]=complex(0,0);
    for (int i=0;i'b')?1:0;
    calc();

    mi[0]=1;for (int i=1;i<=ls;++i) mi[i]=(mi[i-1]<<1)%Mod;
    for (int i=0;iint cnt=F[i<<1];
        if (!cnt) continue;
        ans+=mi[(cnt-1)/2+1]-1;ans%=Mod;
    }
    ans=(ans+Mod)%Mod;
    printf("%d\n",ans);
}

你可能感兴趣的:(题解,manacher,FFT/NTT)