题解 P4199【万径人踪灭】

$\text{Link}$

题意

给一个长度为 $n$ 的,字符集为 $2$ 的字符串 $s$,求满足以下三个条件的字符串 $t$ 的数量。

  • $t\in\text{subsequences}(s)$;
  • $t\notin\text{substrings}(s)$;
  • $t$ 在 $s$ 中的位置、字符沿某对称中心对称。

其中 $n\le 10^5$。

思路

首先求出满足 $1,3$ 条件的 $t$ 的数量减去不满足 $2$ 条件的 $t$ 的数量即可,后者可以 $\text{Manacher}$ 或者 $\text{PAM}$ 解决,不是本题重点,故只讨论前者。

我们可以考虑转换思路,枚举对称中心计算答案之和。

令 $f_i$ 表示以 $i$ 为对称中心时对称的字母个数,则$$f_i=\sum_{j=1}^i[s_j=s_{2i-j}]$$ 容易得知,对于这 $f_i$ 对数中每对都可选或不选,但不能全部不选,所以 $i$ 处的的答案为 $2^{f_i}-1$。

当 $g_i=[s_i=c]$ 时,$s_i=s_j=c$ 当且仅当 $g_i=g_j=1$,即 $g_i\times g_j=1$。

那么枚举 $c$,构造多项式 $F$,其中 $F_i=[s_i=c]$,那么可以直接计算 $F^2$ 计入答案即可。实现时需要注意判断对称中心是在原字符串上还是插入的字符上。

时间复杂度 $O(n\log n)$。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
    //here was something
}
const int N=4e5+10,mod=1e9+7;
char str[N],input[N];
int p[N],ans[N],n,sum;
inline void getc(char *a,char *b,int n){
    int len=0;
    b[len++]='~';
    b[len++]='|';
    for(int i=1;i<=n;i++){
        b[len++]=a[i];
        b[len++]='|';
    }
    b[n*2+2]=0;
    len--;
}
inline int qpow(int x,int y){
    int res=1;
    while(y){
        if(y&1) res=1ll*res*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return res;
} 
#define ld double
const ld pi=acos(-1);
struct comp{
    ld x,y;
    comp(ld a=0,ld b=0){x=a,y=b;}
    comp neg(){ return comp(x,-y); }
    friend comp operator+(const comp &a,const comp &b){ return comp(a.x+b.x,a.y+b.y); }
    friend comp operator-(const comp &a,const comp &b){ return comp(a.x-b.x,a.y-b.y); }
    friend comp operator*(const comp &a,const comp &b){ return comp(a.x*b.x-a.y*b.y,a.y*b.x+a.x*b.y); }
};
int rev[N],lim;
inline int add(int x,int y){ return x+y>=mod?x+y-mod:x+y; }
inline int dec(int x,int y){ return x>=y?x-y:x-y+mod; }
inline void init(int n,int mode=1){
    if(mode){
        int l=0;
        for(lim=1;lim<=n;lim<<=1)l++;
        for(int i=0;i<lim;i++){
            rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
        }
    }else{
        for(lim=1;lim<=n;lim<<=1);
    }
}
inline void FFT(comp *a,int lim,int tpe){
    for(int i=0;i<lim;i++)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
    for(int i=1;i<lim;i<<=1){
        comp wn(cos(pi/i),tpe*sin(pi/i));
        for(int j=0;j<lim;j+=(i<<1)){
            comp w(1);
            for(int k=0;k<i;k++,w=w*wn){
                comp t=a[i+j+k]*w;
                a[i+j+k]=a[j+k]-t;
                a[j+k]=a[j+k]+t;
            }
        }
    }

    if(tpe==-1){
        double p=1./lim;
        for(int i=0;i<lim;i++)
            a[i].x*=p,a[i].y*=p;
    }
}
comp A[N];
int main(){
    n=readstr(input);
    getc(input,str,n);
    for(int i=1,r=0,mid=0;i<=n*2+1;i++){
        if(i<r)
            p[i]=min(p[(mid<<1)-i],r-i);
        while(str[i-p[i]]==str[i+p[i]])
            p[i]++;
        if(i+p[i]>r)
            r=i+p[i],mid=i;
    }
    init(n<<1);
    for(int i=1;i<=n;i++)
        A[i].x=input[i]=='a';
    FFT(A,lim,1);
    for(int i=0;i<lim;i++)
        A[i]=A[i]*A[i];
    FFT(A,lim,-1);
    for(int i=1;i<=n*2+1;i++)
        ans[i]+=(int(A[i].x+0.5)-(i%2==0));
    for(int i=0;i<lim;i++)
        A[i].x=A[i].y=0;
    for(int i=1;i<=n;i++)
        A[i].x=input[i]=='b';
    FFT(A,lim,1);
    for(int i=0;i<lim;i++)
        A[i]=A[i]*A[i];
    FFT(A,lim,-1);
    for(int i=1;i<=n*2+1;i++)
        ans[i]+=(int(A[i].x+0.5)-(i%2==0));
    for(int i=1;i<=n*2+1;i++)
        ans[i]=((ans[i]+(i%2==0))>>1)+(i%2==0);
    for(int i=1;i<=n*2+1;i++)
        ans[i]=qpow(2,ans[i])-1;
    for(int i=1;i<=n*2+1;i++)
        ans[i]-=p[i]>>1;
    for(int i=1;i<=n*2+1;i++)
        sum=(sum+ans[i])%mod;
    printf("%d\n",sum);
    return 0;
}

再见 qwq~

posted @ 2021-07-13 19:01  ffffyc  阅读(8)  评论(0)    收藏  举报  来源