首先正难则反,想到答案即为满足第一条要求的回文子序列数量,减去回文子串数量。回文子串数量 \(hash+\) 二分即可,考虑前半部分。
假如我们将一个回文子序列一层层剥开,就会发现它其实是由多个相同的字母对拼成的。那么容易想到把字母 \(a\) 和字母 \(b\) 的贡献分开计算。那第一条要求就可以转化为所有字母对中心相同,用 \(FFT\) 计算 \(c_k\),表示所有 \(i+j=k\) 的字母对数量即可。
时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
#define int long long
#define ull unsigned int
using namespace std;
const ull q=1145141;
const int N=4e5+5,p=1e9+7;
const double pi=acos(-1);
namespace FFT{
int rev[N],mx,k;
struct comn{double a,b;};
struct dft{comn fg[N];};
comn operator+(comn &x,comn &y){
return {x.a+y.a,x.b+y.b};
}comn operator-(comn &x,comn &y){
return {x.a-y.a,x.b-y.b};
}comn operator*(comn &x,comn &y){
return {x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a};
}void operator+=(comn &x,comn &y){x=x+y;}
void operator-=(comn &x,comn &y){x=x-y;}
void operator*=(comn &x,comn &y){x=x*y;}
void operator+=(dft &x,dft &y){
for(int i=0;i<mx;i++) x.fg[i]+=y.fg[i];
}void operator-=(dft &x,dft &y){
for(int i=0;i<mx;i++) x.fg[i]-=y.fg[i];
}void init(int n){
mx=1,k=0,rev[0]=0;
while(mx<=n) mx*=2,k++;
for(int i=0;i<mx;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}void fft(dft &a,int fl){
for(int i=0;i<mx;i++)
if(i<rev[i]) swap(a.fg[i],a.fg[rev[i]]);
comn o={cos(pi),fl*sin(pi)},w={1,0};
for(int i=1;i<mx;i*=2,o={cos(pi/i),fl*sin(pi/i)})
for(int j=0;j<mx;j+=i*2,w={1,0})
for(int l=j;l<j+i;l++){
comn x=a.fg[l],y=w*a.fg[l+i];
a.fg[l]+=y,a.fg[l+i]=x-y,w*=o;
}
}
}using namespace FFT;
int n,ans;string s;dft t[2];
ull hsh1[N],hsh2[N],qp[N];
int qpow(int x,int y){
int re=1;
while(y){
if(y&1) re=re*x%p;
x=x*x%p,y>>=1;
}return re;
}ull hs1(int l,int r){
return hsh1[l]-hsh1[r]*qp[l-r];
}ull hs2(int l,int r){
return hsh2[l]-hsh2[r]*qp[r-l];
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>s,n=s.size(),qp[0]=1;
s=" "+s,init(2*n);
for(int i=1;i<=n;i++){
t[s[i]-'a'].fg[i].a++,qp[i]=qp[i-1]*q;
hsh1[i]=hsh1[i-1]*q+s[i]-'a';
}for(int i=n;i;i--)
hsh2[i]=hsh2[i+1]*q+s[i]-'a';
fft(t[0],1),fft(t[1],1);
for(int i=0;i<mx;i++){
t[0].fg[i]*=t[0].fg[i];
t[1].fg[i]*=t[1].fg[i];
}fft(t[0],-1),fft(t[1],-1);
for(int i=2;i<=2*n;i++){
int sum=((t[0].fg[i].a+t[1].fg[i].a)/mx+0.5);
ans+=qpow(2,sum/2-(i&1)+1)-1;
}for(int i=1;i<=n;i++){
int l=1,sum=0;
int r=min(i,n-i+1);
while(l<=r){
int mid=(l+r)/2;
if(hs1(i,i-mid)==hs2(i,i+mid))
l=mid+1,sum=mid;else r=mid-1;
}ans-=sum;
}for(int i=1;i<n;i++){
int l=1,sum=0;
int r=min(i,n-i);
while(l<=r){
int mid=(l+r)/2;
if(hs1(i,i-mid)==hs2(i+1,i+mid+1))
l=mid+1,sum=mid;else r=mid-1;
}ans-=sum;
}cout<<(ans%p+p)%p;
return 0;
}
标签:BZOJ3160,return,int,题解,void,万径,ull,comn,operator
From: https://www.cnblogs.com/chang-an-22-lyh/p/18680706/bzoj3160-wan_jing_ren_zong_mie-tj