[Loj 6070][回文树+可持久化线段树+border理论]基因

题意
给你一个长度为n的字符串,q次询问一个区间内本质不同的回文串的个数。
n ⩽ 100000 , q ⩽ 100000 n\leqslant 100000,q\leqslant 100000 n100000,q100000
解法
考虑用可持久化线段树。
f i , j f_{i,j} fi,j表示区间 [ j , i ] [j,i] [j,i]的答案。
尝试找到 f i − 1 到 f i f_{i-1}到f_{i} fi1fi的递推关系。
我们建出回文树。
注意到,回文树上每个节点到根可以拆成 l o g n logn logn个等差数列。
对这 l o g n logn logn个等差数列稍微分析一下。
你会发现每个等差数列对应着一个从 f i − 1 f_{i-1} fi1 f i f_i fi的区间加法。
证明需要用到一些回文串border的理论。
相关资料可以看我的另一篇博客:
https://blog.csdn.net/ezoilearner/article/details/84851672
于是时空复杂度 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn)

#include
#include
#include
#include
using namespace std;
int type;
int n,q;
#define Maxn 100010
#define V 10000010
char s[Maxn];

struct Node{
    int sum;
    int ls,rs;
}tree[V];
int root[Maxn],cnt=0;
void Add(int &k,int x,int l,int r,int L,int R){
    k=++cnt;
    tree[k]=tree[x];
    if(l==L&&r==R){
        tree[k].sum++;
        return;
    }
    int mid=(l+r)>>1;
    if(R<=mid)Add(tree[k].ls,tree[x].ls,l,mid,L,R);
    else if(mid>1;
    if(pos<=mid)return Query(tree[k].ls,l,mid,pos)+tree[k].sum;
    else return Query(tree[k].rs,mid+1,r,pos)+tree[k].sum;
}

int maxv[Maxn<<2];
void Modify(int k,int l,int r,int pos,int num){
    if(l==r){
        maxv[k]=num;
        return;
    }
    maxv[k]=num;
    int mid=(l+r)>>1;
    if(pos<=mid)Modify(k<<1,l,mid,pos,num);
    else Modify(k<<1|1,mid+1,r,pos,num);
}
int Query(int k,int l,int r,int L,int R){
    if(l==L&&r==R)return maxv[k];
    int mid=(l+r)>>1;
    if(R<=mid)return Query(k<<1,l,mid,L,R);
    else if(mid1&&len[tot]-len[fail[tot]]==len[fail[tot]]-len[fail[fail[tot]]])?anc[fail[tot]]:tot);
    }
    last=ch[tmp][dir];
}

int head[Maxn],v[Maxn],nxt[Maxn],T=0;
int in[Maxn],out[Maxn],dfk=0;
inline void add_edge(int s,int e){T++;v[T]=e;nxt[T]=head[s];head[s]=T;}
void dfs(int u){
    in[u]=++dfk;
    for(int i=head[u];i;i=nxt[i])
        dfs(v[i]);
    out[u]=dfk;
}
inline void Build_Tree(){
    for(register int i=1;i<=n;++i){
        insert(s[i]-'a');
        endpos[i]=last;
    }
    add_edge(1,0);
    for(register int i=2;i<=tot;++i)add_edge(fail[i],i);
    dfs(1);
    for(register int i=1;i<=n;++i){
        root[i]=root[i-1];
        for(register int k=endpos[i];k>1;k=fail[anc[k]]){
            int l=max(Query(1,1,dfk,in[k],out[k])-len[k]+1,0)+1;
            int r=i-len[anc[k]]+1;
            Add(root[i],root[i],1,n,l,r);
        }
        Modify(1,1,dfk,in[endpos[i]],i);
    }
}

inline void rd(int &x){
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
}

int main(){
    rd(type);
    rd(n);rd(q);
    memset(ch[0],0,sizeof(ch[0]));
    memset(ch[1],0,sizeof(ch[1]));
    fail[0]=1;len[1]=-1;
    anc[0]=0;anc[1]=1;s[0]=-1;
    scanf("%s",s+1);
    Build_Tree();
    int Ans=0,l,r;
    while(q--){
        rd(l);rd(r);
        l=l^(type*Ans);r=r^(type*Ans);
        printf("%d\n",Ans=Query(root[r],1,n,l));
    }
    return 0;
}

你可能感兴趣的:(日常习题)