1. 程式人生 > >BZOJ3160 萬徑人蹤滅

BZOJ3160 萬徑人蹤滅

www. 最長回文 部分 lin 就是 etc printf -o 題解

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 萬徑人蹤滅