BZOJ-3879: SvT

后缀树+虚树

做这道题我们需要知道这么几个东西

  • 两个后缀的\(lcp\)就是其后缀树上的\(lca\)

  • 用一个字符串的反串建\(sam\),其\(parent\)边就构成了原串的后缀树

这道题要求询问\(k\)个后缀两两之间\(lcs\)之和,转化到后缀树中就是问这\(k\)个点任意两个点\(lca\)处值相加

考虑树形\(DP\),计算每个点是多少对点的\(LCA\),然而这样时间复杂度一次\(O(n)\),总共\(O(m*n)\)

对于每一次询问的\(k\)个点,在后缀树上建虚树,时间复杂度就是\(O(\sum t)\)

#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+100,maxm=1e6+100;
int n,t,m,a[maxn],fa[maxn],dep[maxn],son[maxn],siz[maxn],top[maxn],bj[maxn];
int nex[maxm],v[maxm],head[maxn],num=1,dfn[maxn],tim,tp,st[maxn],bh[maxn];
char s[maxn];
vector<int>e[maxn];
void add(int x,int y){
    v[++num]=y;
    nex[num]=head[x];
    head[x]=num;
}
struct SAM{
    int son[maxn][26],len[maxn],fa[maxn];
    int last,tot;
    SAM(){last=tot=1;}
    void insert(int x){
        int p=last,np=++tot;
        len[np]=len[p]+1;
        while(p&&son[p][x]==0)
            son[p][x]=np,p=fa[p];
        if(!p)
            fa[np]=1;
        else{
            int q=son[p][x];
            if(len[q]==len[p]+1)
                fa[np]=q;
            else{
                int nq=++tot;
                memcpy(son[nq],son[q],sizeof(son[q]));
                fa[nq]=fa[q];
                len[nq]=len[p]+1;
                fa[q]=fa[np]=nq;
                while(p&&son[p][x]==q)
                    son[p][x]=nq,p=fa[p];
            }
        } 
        last=np;
    }
    void work(){
        for(int i=2;i<=tot;i++)
            add(fa[i],i);
    }
}sam;
void dfs1(int x,int f,int d){
    fa[x]=f;
    dep[x]=d;
    siz[x]=1;
    dfn[x]=++tim;
    for(int i=head[x];i;i=nex[i])
        if(v[i]!=f){
            dfs1(v[i],x,d+1);
            if(siz[son[x]]<siz[v[i]])
                son[x]=v[i];
            siz[x]+=siz[v[i]];
        }
}
void dfs2(int x,int tp){
    top[x]=tp;
    if(son[x]) dfs2(son[x],tp);
    for(int i=head[x];i;i=nex[i])
        if(v[i]!=fa[x]&&v[i]!=son[x])
            dfs2(v[i],v[i]);
}
int Lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])
            swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]>dep[y]?y:x;
}
bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}
void insert(int x){
    if(!tp){
        st[++tp]=x;
        return;
    }
    int lca=Lca(x,st[tp]);
    while(tp>1&&dfn[st[tp-1]]>=dfn[lca])
        e[st[tp-1]].push_back(st[tp]),tp--;
    if(st[tp]!=lca) e[lca].push_back(st[tp]),st[tp]=lca;
    st[++tp]=x;
}
ll dp(int x,ll &z){
    ll tmp=0,tot=bj[x];
    for(int i=0;i<e[x].size();i++){
        int y=e[x][i];
        tmp=dp(y,z);
        z+=tot*tmp*sam.len[x];
        tot+=tmp;
    }
    bj[x]=0;
    e[x].clear();
    return tot;
}
void vtree(){
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]),a[i]=bh[a[i]],bj[a[i]]=1;
    sort(a+1,a+m+1,cmp);
    st[tp=1]=1;
    a[0]=-1;
    for(int i=1;i<=m;i++) if(a[i]!=a[i-1]) insert(a[i]);
    while(tp>1) e[st[tp-1]].push_back(st[tp]),tp--;tp--;
    ll ans=0;
    dp(1,ans);
    printf("%lld\n",ans);
}
int main(){
    scanf("%d%d",&n,&t);
    scanf("%s",s+1);
    for(int i=n;i>=1;i--) sam.insert(s[i]-'a'),bh[i]=sam.last;
    sam.work();
    dfs1(1,1,1);
    dfs2(1,1);
    while(t--)
        vtree();
    return 0;
}
posted @ 2018-12-04 21:02  nianheng  阅读(92)  评论(0编辑  收藏  举报