[BZOJ3160] 万径人踪灭 题解

首先正难则反,想到答案即为满足第一条要求的回文子序列数量,减去回文子串数量。回文子串数量 \(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;
}
posted @   长安一片月_22  阅读(5)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示