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;
}