BZOJ 3879: SvT [虚树 后缀树]

传送门

题意:

多次询问,给出一些后缀,求两两之间$LCP$之和


 

哈哈哈哈哈哈哈竟然$1A$了,刚才还在想如果写不好这道题下节数学就不上了,看来是上天让我上数学课啊

$Suffix\ Virtual\ Tree$

没有多次询问就是那道差异了

多次询问总次数$O(n)$,建出后缀树每次建虚树就行了

然后询问给出的是后缀,用一个$pos$映射到后缀树上的点

然后$Right$集合要在$DP$的时候递推

 

貌似还有后缀数组的做法跑的好快

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e6+5,INF=1e9;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n,Q;
char s[N];
struct State{
    int ch[26],par,val;
}t[N];
int sz=1,root=1,last=1;
int pos[N];
void extend(int c){
    int p=last,np=++sz; 
    t[np].val=t[p].val+1;
    for(;p&&!t[p].ch[c];p=t[p].par) t[p].ch[c]=np;
    if(!p) t[np].par=root;
    else{
        int q=t[p].ch[c];
        if(t[q].val==t[p].val+1) t[np].par=q;
        else{
            int nq=++sz;
            t[nq]=t[q];t[nq].val=t[p].val+1;
            t[q].par=t[np].par=nq;
            for(;p&&t[p].ch[c]==q;p=t[p].par) t[p].ch[c]=nq;
        }
    }
    last=np;
}


struct Edge{
    int v,ne;
}e[N];
int cnt,h[N];
inline void ins(int u,int v){
    cnt++;
    e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
}
int dfn[N],dfc,fa[N][21],deep[N];
void dfs(int u){
    dfn[u]=++dfc;
    for(int i=1;(1<<i)<=deep[u];i++)
        fa[u][i]=fa[ fa[u][i-1] ][i-1];
    for(int i=h[u];i;i=e[i].ne) 
        if(e[i].v!=fa[u][0]){
            fa[e[i].v][0]=u; 
            deep[e[i].v]=deep[u]+1;
            dfs(e[i].v);
        }
}
inline int lca(int x,int y){
    if(deep[x]<deep[y]) swap(x,y);
    int bin=deep[x]-deep[y];
    for(int i=19;i>=0;i--)
        if((1<<i)&bin) x=fa[x][i];
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return x==y ? x : fa[x][0];
}

inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
int st[N],key[N],a[N];
int d[N];
ll ans;
void dp(int u){
    d[u]=key[u] ? 1 : 0;
    for(int i=h[u];i;i=e[i].ne){
        dp(e[i].v);
        ans+=(ll)d[u]*d[e[i].v]*t[u].val;
        d[u]+=d[e[i].v];
    }
    h[u]=0;
}
void VirTree(){
    cnt=0;
    int n=read();
    for(int i=1;i<=n;i++) a[i]=pos[read()];
    sort(a+1,a+1+n);
    int p=0;a[++p]=a[1];
    for(int i=2;i<=n;i++) if(a[i]!=a[i-1]) a[++p]=a[i];
    n=p;
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++) key[a[i]]=1;

    int top=0;
    for(int i=1;i<=n;i++){
        if(!top) {st[++top]=a[i];continue;}
        int x=a[i],f=lca(x,st[top]);
        while(dfn[f]<dfn[st[top]]){
            if(dfn[f]>=dfn[st[top-1]]){
                ins(f,st[top--]);
                if(f!=st[top]) st[++top]=f;
                break;
            }else ins(st[top-1],st[top]),top--;
        }
        st[++top]=x;
    }
    while(top>1) ins(st[top-1],st[top]),top--;

    ans=0;
    dp(st[1]);
    printf("%lld\n",ans);
    for(int i=1;i<=n;i++) key[a[i]]=0;
}

int main(){
    freopen("in","r",stdin);
    n=read();Q=read();
    scanf("%s",s+1);
    reverse(s+1,s+1+n);
    for(int i=1;i<=n;i++) extend(s[i]-'a'),pos[n-i+1]=last;
    for(int i=1;i<=sz;i++) ins(t[i].par,i);
    dfs(root);
    memset(h,0,sizeof(h));
    while(Q--) VirTree();
}    

 

posted @ 2017-03-10 09:52  Candy?  阅读(526)  评论(0编辑  收藏  举报