bzoj 3160

首先简化一下题意:

求一个字符串的子序列个数,要求这个子序列满足:是一个回文序列,且在原串中不连续

怎么搞?

设这个字符串为S

首先上一个容斥:我们找出所有回文子序列,然后减去连续的部分即可

而连续的部分可以用manacher算出来

所以我们重点研究一下如何找出所有回文子序列

首先我们回到manacher算法的思想:如果我想找出回文子序列,那么我们不如去找对称轴!(在接下来的介绍中不区分对称轴和对称中心)

而同理,对称轴会有两种情况:以一个字符为对称中心的和以一个空位为对称中心的

所以我们分类讨论:

①.设我们以位置$i$上的字符为对称轴,那么如果我们能找到两个位置$x,y$,使得$x+y=2*i$,且$S_x==S_y$,那么很显然,选出$(S_x,S_i,S_y)$这一个子序列是合法的

那么,如果我们能找到k组这样的x,y,那么以位置i上字符为对称轴的方案就是$2^(k+1)-1$

这一点很好理解,因为算上中间的i,一共有$k+1$组可以支持选或不选,但是要减去所有都不选的情况

②.设我们以位置$i$与$i+1$之间的空格为对称轴,那么如果我们能找到两个位置$x,y$,满足$x+y=2*i+1$,且$S_x==S_y$,那么很显然,选出$(S_x,S_y)$这一个子序列是合法的

那么,如果我们能找到k组这样的x,y,那么以位置i与i+1之间空格为对称轴的方案就是$2^k-1$

我们看到,设字符串长度为$n$,如果有$S_x==S_y$且对称轴为i(假设以一个字符对称),那么一定有$S_x==S_2*i-x$!

那么,由于只有a,b两种字符,所以我们可以分开匹配a,b两种字符

在匹配a字符时,我们可以设所有为a的位置值为1,而所有为b的位置值为0,那么可以发现,如果$S_x*S_2*i-x==1$,那么就可以说明这两个位置上是对应的a!

那么这是不是有点类似卷积的形式了?

可以发现,如果我们按上述说法将字符串转成一个序列,然后对这个序列自己与自己卷积,会得到一个长度为2*n的序列,其中下标为奇数的就是初始对称轴为空格的情况,下标为偶数的就是初始对称轴为一个字符的情况

那么做法是不就很显然了?

我们按a对字符串转成一个序列与自己卷积,记录对应位置的答案,再按b对字符串转成一个序列与自己卷积,将对应位置的答案累计上去,然后每一位分奇偶算2的幂次-1,最后减掉manacher做出的连续部分即可

注意在累计时,由于每一组对应的$S_x$与$S_2*i-x$在卷积时事实上被记录了两次,所以每一个位置都应该除2再做2的幂次!!

贴代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
const ll mode=1000000007;
const double pi=acos(-1.0);
int to[(1<<20)+5];
struct cp
{
    double x,y;
};
cp operator + (cp a,cp b)
{
    return (cp){a.x+b.x,a.y+b.y};
}
cp operator - (cp a,cp b)
{
    return (cp){a.x-b.x,a.y-b.y};
}
cp operator * (cp a,cp b)
{
    return (cp){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}
void FFT(cp *a,int len,int k)
{
    for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);
    for(int i=1;i<len;i<<=1)
    {
        cp w0=(cp){cos(pi/i),k*sin(pi/i)};
        for(int j=0;j<len;j+=(i<<1))
        {
            cp w=(cp){1,0};
            for(int o=0;o<i;o++,w=w*w0)
            {
                cp w1=a[j+o];
                cp w2=a[j+o+i]*w;
                a[j+o]=w1+w2;
                a[j+o+i]=w1-w2;
            }
        }
    }
}
char ch[100005];
char temp[200005];
int p[200005];
ll f[200005];
int n,maxp,p0;
ll sum=0;
int lim=1,l;
void manacher()
{    
    temp[0]=temp[1]='#';
    for(int i=1;i<=n;i++)temp[i<<1]=ch[i],temp[(i<<1)|1]='#';
    p[0]=1;
    int l=(n<<1)+2;
    temp[l]='0';
    for(int i=1;i<l;i++)
    {
        if(i<maxp)p[i]=min(p[(p0<<1)-i],p[p0]+p0-i);
        else p[i]=1;
        while(temp[i-p[i]]==temp[i+p[i]])p[i]++;
        if(p[i]+i>maxp)maxp=p[i]+i,p0=i;
    }
    for(int i=1;i<=l;i++)sum+=p[i]/2;
}
cp a[(1<<20)+5],b[(1<<20)+5],c[(1<<20)+5];
ll pow_mul(ll x,ll y)
{
    ll ans=1;
    while(y)
    {
        if(y&1)ans*=x,ans%=mode;
        x*=x,x%=mode,y>>=1;
    }
    return ans;
}
int main()
{
    scanf("%s",ch+1);
    n=strlen(ch+1);
    manacher();
    for(int i=1;i<=n;i++)a[i-1].x=(ch[i]=='a');
    while(lim<=2*n)lim<<=1,l++;
    for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(l-1)));
    FFT(a,lim,1);
    for(int i=0;i<lim;i++)c[i]=a[i]*a[i];
    FFT(c,lim,-1);
    for(int i=0;i<2*n-1;i++)f[i]+=(ll)(c[i].x/lim+0.5);
    for(int i=1;i<=n;i++)b[i-1].x=(ch[i]=='b');
    FFT(b,lim,1);
    for(int i=0;i<lim;i++)c[i]=b[i]*b[i];
    FFT(c,lim,-1);
    for(int i=0;i<2*n-1;i++)f[i]+=(ll)(c[i].x/lim+0.5);
    ll ans=0;
    for(int i=0;i<2*n-1;i++)
    {
        if(i&1)ans+=pow_mul(2,f[i]/2)-1,ans%=mode;
        else ans+=pow_mul(2,f[i]/2+1)-1,ans%=mode;
    }
    printf("%lld\n",((ans-sum)%mode+mode)%mode);
    return 0;
}

 

posted @ 2019-05-02 18:18  lleozhang  Views(156)  Comments(1Edit  收藏  举报
levels of contents