BZOJ3879:SvT(后缀自动机+虚树)

来源:互联网 发布:人体尺度数据测绘图 编辑:程序博客网 时间:2024/06/11 13:08

日更两篇,真刺激。

题目
题意:给出一个串,若干个询问。每个询问给出一些后缀,问它们两两之间LCP的长度和。

后缀数组的写法太简单显然了,把后缀按顺序拿出来,只用一个单调栈就可以了,但我并不会…所以就是SAM了。

经过一轮求后缀LCP模型在SAM上乱套,发现就是反串建SAM,得到后缀树。后缀树上结点的LCA的长度就是LCP长度。对于这里,把询问给出的结点在后缀树上建出虚树,每个点算贡献即可。
具体是这样的,对于x的两个儿子son1和son2,siz[son1]siz[son2]dep[x]就能贡献答案。

#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int N=1001000;const LL mo=23333333333333333;void read(int &hy){    hy=0;    char cc=getchar();    while(cc<'0'||cc>'9')    cc=getchar();    while(cc>='0'&&cc<='9')    {        hy=(hy<<3)+(hy<<1)+cc-'0';        cc=getchar();    }}int n,m,k,a[N];int son[N][26],pre[N],dep[N],w[N],cnt=1,last=1;int head[N],nex[N],to[N],cur;int Chtholly[N],top[N],len[N],tim[N],siz[N],times;int st[N];char cc[N];LL ans;bool vis[N];void Insert(int x,int num){    dep[++cnt]=dep[last]+1;    w[num]=cnt;    int np=cnt,p=last;    last=cnt;    for(;!son[p][x];p=pre[p])    son[p][x]=np;    if(!p)    pre[np]=1;    else    {        int q=son[p][x];        if(dep[q]==dep[p]+1)        pre[np]=q;        else        {            dep[++cnt]=dep[p]+1;            int nq=cnt;            pre[nq]=pre[q];            pre[q]=pre[np]=nq;            mmcp(son[nq],son[q]);            for(;son[p][x]==q;p=pre[p])            son[p][x]=nq;        }    }}void add(int u,int v){    to[++cur]=v;    nex[cur]=head[u];    head[u]=cur;}void dfs(int x){    siz[x]=1;    tim[x]=++times;    for(int h=head[x];h;h=nex[h])    {        len[to[h]]=len[x]+1;        dfs(to[h]);        siz[x]+=siz[to[h]];        if(siz[to[h]]>siz[Chtholly[x]])        Chtholly[x]=to[h];    }}void dfs2(int x,int tp){    top[x]=tp;    if(Chtholly[x])    dfs2(Chtholly[x],tp);    for(int h=head[x];h;h=nex[h])    if(to[h]!=Chtholly[x])    dfs2(to[h],to[h]);}int lca(int x,int y){    while(top[x]!=top[y])    {        if(len[top[x]]<len[top[y]])        swap(x,y);        x=pre[top[x]];    }    return len[x]<len[y] ? x : y;}bool cmp(int x,int y){    return tim[x]<tim[y];}void dp(int x){    siz[x]=vis[x] ? 1 : 0;    for(int h=head[x];h;h=nex[h])    {        dp(to[h]);        ans+=(LL)siz[to[h]]*siz[x]*dep[x];        siz[x]+=siz[to[h]];    }    head[x]=0;}void work(){    ans=0;    k=cur=0;    int kk;    read(kk);    for(int i=1;i<=kk;i++)    {        int by;        read(by);        if(!vis[w[by]])        a[++k]=w[by];        vis[w[by]]=1;    }    sort(a+1,a+k+1,cmp);    int t=0;    for(int i=1;i<=k;i++)    {        if(!t)        {            st[++t]=a[i];            continue;        }        int lc=lca(a[i],st[t]);        while(lc!=st[t])        {            if(tim[lc]>=tim[st[t-1]])            {                add(lc,st[t--]);                if(lc!=st[t])                st[++t]=lc;                break;            }            else            add(st[t-1],st[t]),t--;        }        st[++t]=a[i];    }    while(t!=1)    add(st[t-1],st[t]),t--;    dp(st[1]);    for(int i=1;i<=k;i++)    vis[a[i]]=0;    printf("%lld\n",ans);}int main(){    cin>>n>>m;    scanf("%s",cc);    for(int i=n;i>=1;i--)    Insert(cc[i-1]-'a',i);    for(int i=2;i<=cnt;i++)    add(pre[i],i);    dfs(1);    dfs2(1,1);    mmst(head,0);    while(m--)    work();    return 0;}

这里写图片描述

原创粉丝点击