LOJ6070 基因 分块+回文自动机

这个在翁文涛的论文里有讲到

大概的就是一个子串的回文自动机是原串回文自动机的子图
于是每隔\(\sqrt n\)重新跑一个\((k \times \sqrt n,n)\)的回文自动机 记录回文串个数和位置 并且分别维护后缀的\(fail\)和前缀的\(fail\)

每次询问\((l,r)\)只需要把\((k \times\sqrt n,r)\)的答案直接加上 再暴力添加\((l,(k \times\sqrt n)-1)\)这一段就可以得到\(ans\)

只理解了大概 只能以后遇到题再加强了

\(update:\)新写了一篇\(BZOJ5384\)的博客,但是用那一题的方法加上主席树可以做到更优的复杂度

#include<bits/stdc++.h>
using namespace std;
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define pa pair<int,int>
#define mod 1000000007
#define ll long long
#define mk make_pair
#define pb push_back
#define fi first
#define se second
#define cl(x) memset(x,0,sizeof x)
#ifdef Devil_Gary
#define bug(x) cout<<(#x)<<" "<<(x)<<endl
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define bug(x)
#define debug(...)
#endif
const int INF = 0x7fffffff;
const int N=1e5+5;
const int M=355;
/*
char *TT,*mo,but[(1<<15)+2];
#define getchar() ((TT==mo&&(mo=(TT=but)+fread(but,1,1<<15,stdin),TT==mo))?-1:*TT++)//*/
inline int read(){
    int x=0,rev=0,ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')rev=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return rev?-x:x;
}
int type,n,S,T,Q,a[N],Ans,id=1;
int ans[M][N],p[M][N],pos[M][N];
int pb,pf,c[N][26],qf[N][26],f[N],len[N],vis[N];
char s[N];
void exback(int l,int i){
    int x=a[i];
    if(i-len[pb]-1<l||a[i-len[pb]-1]!=x) pb=qf[pb][x];
    if(!c[pb][x]){
        len[++id]=len[pb]+2;
        int k=f[pb];
        if(a[i-len[k]-1]!=x) k=qf[k][x]; k=c[k][x];
        memcpy(qf[id],qf[k],sizeof qf[k]);
        qf[id][a[i-len[k]]]=k,f[id]=k,c[pb][x]=id;
    }
    pb=c[pb][x];
    if(len[pb]==i-l+1) pf=pb;
}
void exfront(int i,int r){
    int x=a[i];
    if(i+len[pf]+1>r||a[i+len[pf]+1]!=x) pf=qf[pf][x];
    if(!c[pf][x]){
        len[++id]=len[pf]+2;
        int k=f[pf];
        if(a[i+len[k]+1]!=x) k=qf[k][x];k=c[k][x];
        memcpy(qf[id],qf[k],sizeof qf[k]);
        qf[id][a[i+len[k]]]=k,f[id]=k,c[pf][x]=id;
    }
    pf=c[pf][x];
    if(len[pf]==r-i+1) pb=pf; 
}
int calc(int x){
    return (x-1)/S+1;
}
int main(){
#ifdef Devil_Gary
    freopen("in.txt","r",stdin);
#endif
    type=read(),n=read(),Q=read(),S=sqrt(n),scanf("%s",s+1);
    for(int i=1;i<=n;i++) a[i]=s[i]-'a';
    f[0]=f[1]=1,len[1]=-1;
    for(int i=0;i<26;i++) qf[0][i]=1;
    memset(pos,127/3,sizeof pos);
    for(int L=1,i=1;L<=n;L+=S,i++){
        pb=pf=0,++T;
        for(int j=L;j<=n;j++){
            exback(L,j);
            ans[i][j]=ans[i][j-1],p[i][j]=pf;
            if(vis[pb]<T) vis[pb]=T,pos[i][pb]=j,ans[i][j]++;
        }
    }
    while(Q--){
        int L=read(),R=read();
        if(type) L^=Ans,R^=Ans;Ans=0;
        if(calc(L)==calc(R)){
            pb=0,++T;
            for(int j=L;j<=R;j++){
                exback(L,j);
                if(vis[pb]<T) ++Ans,vis[pb]=T;
            }
        }
        else{
            int i=calc(L);Ans=ans[i+1][R];
            pf=p[i+1][R],++T;
            for(int j=i*S;j>=L;j--){
                exfront(j,R);
                if(vis[pf]<T) vis[pf]=T,Ans+=pos[i+1][pf]>R;
            }
        }
        printf("%d\n",Ans);
    }
}

转载于:https://www.cnblogs.com/devil-gary/p/9213689.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值