BZOJ3160 万径人踪灭

Posted guessycb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ3160 万径人踪灭相关的知识,希望对你有一定的参考价值。

BZOJ3160 万径人踪灭

题目大意

给定一个字符串\(S\)\(|S|=n\),字符集为\(\{a,b\}\)
现在要求满足下面条件的子序列个数:

  • 满足其存在一个对称中心(是回文子序列)。
  • 至少分为3段,即不能只有连续一段。

我怕我的题意归纳出锅所以放一下题目网址:web
问满足条件的子序列个数。数据范围:\(n \leq 10^5\)

题解

首先先\(manacher\)那一套转换一下字符串(间隙间加#)。
假设没有不能连续的限制。
如果我们知道以某个点\(i\)为对称中心的左右两边对称点对\(t_i\)个。
那么符合条件的子序列个数就是\(2^{t_i} - 1\)个(减去空串)。
怎么求这个东西呢?
\(s[x],s[y]\)关于\(s[e]\)对称,那么它们的下标满足\(x + y = 2e\)
长得像一个卷积啊......
所以说,对于一个字符\(c\),若\(s[i]=c\),那么多项式\(f[i]=1\),否则为\(0\)
两个这样的多项式卷积\(f\)卷起来就可以得到所有的\(t_i\)了。
然后考虑删掉不合法的情况(即只有连续一段的情况)。
这个简直弱智啊,\(manacher\)一下求出最长回文半径\(P\)不就行了吗?
注意回文串中虚拟字符#的影响,在对应部分对应的除2即可。

实现代码

我发现我还是记得打\(manacher\)的.....(O-O)。

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define _ 800005
#define ll long long
#define mod 1000000007
using namespace std;

IL int gi(){
    RG int data = 0 , m = 1; RG char ch = 0;
    while(ch != '-' && (ch<'0' || ch > '9')) ch = getchar();
    if(ch == '-'){m = 0; ch = getchar();}
    while(ch>='0' && ch<='9'){data = (data<<1) + (data<<3) + ch - '0' ;  ch = getchar();}
    return (m) ? data : -data ; 
}

const double PI = acos(-1) ; 
int R[_],P[_],mxpos,lr,ps,len,lg,nn,mm,db[_]; ll ans,ret[_]; char ycb[_],s[_] ; 

struct Complex{
    double r , i ; 
    IL Complex(){i = 0.0; r = 0.0; }
    IL Complex(double a,double b){r = a; i = b ; }
    IL Complex operator + (Complex A){ return Complex(r+A.r,i+A.i) ; }
    IL Complex operator - (Complex A){ return Complex(r-A.r,i-A.i) ; }
    IL Complex operator * (Complex A){
        return Complex(A.r*r - i*A.i , A.r*i + A.i*r) ; 
    }
}f1[_],f2[_],X,Y;

void FFT(Complex *F,int opt){
    for(RG int i = 0; i < nn; i ++)
        if(i < R[i]) swap(F[i] , F[R[i]]) ;
    for(RG int i = 1; i < nn; i <<= 1){
        Complex W(cos(PI/i) , opt*sin(PI/i)) ;
        for(RG int j = 0,e = (i<<1) ; j < nn; j += e){
            Complex w(1,0) ;
            for(RG int k = 0; k < i; k ++,w = w*W){
                X = F[j+k] ; Y = w * F[j+k+i] ;
                F[j+k] = X + Y ; F[j+k+i] = X - Y ; 
            }
        }
    }if(opt==-1)for(RG int i = 0; i < nn; i ++) F[i].r = F[i].r / nn ; 
}

IL void solve(char ch){
    mm = 2*lg ;  lr = 0;
    for(nn = 1; nn <= mm; nn<<=1) ++ lr ; lr --;
    for(RG int i = 0; i < nn; i ++)
        R[i] = (R[i>>1]>>1) | ((i&1) << lr) ;
    for(RG int i = 0; i <= nn; i ++)
        f1[i].r = f1[i].i = f2[i].r = f2[i].i = 0 ; 
    for(RG int i = 0; i <= lg; i ++)
        if(s[i] == ch)f1[i].r = f2[i].r = 1; else f1[i].r = f2[i].r = 0;    
    FFT(f1 , 1) ; FFT(f2 , 1) ;
    for(RG int i = 0; i <= nn; i ++) f1[i] = f1[i] * f2[i] ;
    FFT(f1 , -1) ;
    for(RG int i = 0; i <= lg; i ++) ret[i] = (ret[i] + ((long long)(f1[i<<1].r+0.5)+1)/2 ) % mod ;
    return ; 
}

IL void manacher(){
    P[1] = 1 ; mxpos = 2; ps = 1;
    for(RG int i = 2; i <= lg; i ++){
        P[i] = (mxpos > i) ? min(mxpos - i , P[(ps<<1) - i]) : 0 ;
        while(i-P[i]-1>=0 && i+P[i]+1<= lg && s[i-P[i]-1] == s[i+P[i]+1]) ++ P[i] ;
        if(i+P[i] >= mxpos) mxpos = i+P[i] , ps = i ;
    }return ; 
}

int main(){
    scanf("%s",ycb) ; len = strlen(ycb) ;
    for(RG int i = 0; i <= len; i ++) s[i<<1] = '#' , s[i<<1|1] = ycb[i] ;
    lg = (len << 1) ;
    solve('a') ;  solve('b') ;
    db[0] = 1;
    for(RG int i = 1; i <= lg; i ++) db[i] = 1ll * db[i-1] * 2 % mod ;
    for(RG int i = 1; i <= lg; i ++) ans = (ans + db[ret[i]] - 1) % mod ;;
    manacher();
    for(RG int i = 1; i <= lg; i ++) ans = ((ans - (P[i]+1)/2) % mod + mod) % mod ;
    printf("%lld\n" , ans) ; return 0;
}

以上是关于BZOJ3160 万径人踪灭的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ3160 万径人踪灭

BZOJ 3160 万径人踪灭

bzoj3160万径人踪灭 FFT

BZOJ3160万径人踪灭 Manacher+FFT

bzoj3160 万径人踪灭

[BZOJ 3160] 万径人踪灭