bzoj 3879: SvT 后缀自动机+后缀树+虚树

10 篇文章 0 订阅
1 篇文章 0 订阅

题目大意:给定一个字符串,多次询问一些后缀两两之间的最长前缀和。
题解:首先可以很简单的看出是后缀树,然后就很自然地想到用后缀自动机来构建后缀树,然后就变成了一道裸的虚树DP。真是说起来容易啊,写的时候要注意一些问题,代表后缀的节点为第一次插入的节点,中间建的nq起到辅助节点的作用,然后,,,好像也没啥了,写了就知道了QAQ(这题卡时QAQ)

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
namespace IStream
{
    char get_char()
    {
        static char *C,*mempool;
        const int L=1<<20;
        static char buffer[L];
        if(C==mempool)
            mempool=(C=buffer)+fread(buffer,1,L,stdin);
        if(C==mempool) return EOF;
        return *C++;
    }
    int get_int()
    {
        char c;
        do c=get_char();while((c<'0' || c>'9') && c!='-');
        int flag=1;
        if(c=='-') flag=-1;
        int re=0;
        while(c>='0' && c<='9')
        {
            re=(re<<1)+(re<<3)+c-'0';
            c=get_char();
        }
        return flag*re;
    }
}
struct sam
{
    int max_len,id;
    sam *son[26],*parent;
    sam(){}
}mempool[1000000],*root=&mempool[1],*last=root;
int now=1;
void Insert(int zm)
{
    sam *p=last;
    now++;
    sam *np=&mempool[now];
    mempool[now].id=now;
    np->max_len=p->max_len+1;
    while(p && !p->son[zm])
    {
        p->son[zm]=np;
        p=p->parent;
    }
    if(!p) np->parent=root;
    else
    {
        sam *q=p->son[zm];
        if(q->max_len==p->max_len+1) np->parent=q;
        else
        {
            now++;
            sam *nq=&mempool[now];
            mempool[now].id=now;
            nq->max_len=p->max_len+1;
            memcpy(nq->son,q->son,sizeof(nq->son));
            nq->parent=q->parent;
            q->parent=nq;
            np->parent=nq;
            while(p && p->son[zm]==q)
            {
                p->son[zm]=nq;
                p=p->parent;
            }
        }
    }
    last=np;
}
char s[1000000];
int pos[1000000];
long long siz[1000000];
struct bian
{
    int l,r;
}a[1000000];
int fir[1000000];
int timf[1000000];
int nex[1000000];
int pd[1000000];
int T;
int tot=1;
void add_edge(int l,int r)
{
    a[++tot].l=l;
    a[tot].r=r;
    if(timf[l]!=T)
    {
        fir[l]=0;
        timf[l]=T;
    }
    nex[tot]=fir[l];
    fir[l]=tot;
}
int fa[1000000][21];
int id[1000000];
int deep[1000000];
int cnt=0;
void dfs(int u,int fro)
{
    fa[u][0]=fro;
    id[u]=++cnt;
    deep[u]=deep[fro]+1;
    for(int o=fir[u];o;o=nex[o])
    {
        if(a[o].r==fro) continue;
        dfs(a[o].r,u);
    }
}
void init()
{
    for(int j=1;j<=19;j++)
        for(int i=1;i<=now;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
}
long long ans=0;
void dp(int u,int fro)
{
    siz[u]=(pd[u]==T?1:0);
    long long v=(u==1?0:mempool[u].max_len-mempool[fro].max_len);
    if(timf[u]!=T)
    {
        timf[u]=T;
        fir[u]=0;
    }
    for(int o=fir[u];o;o=nex[o])
    {
        dp(a[o].r,u);
        siz[u]+=siz[a[o].r];
    }
    ans+=v*siz[u]*(siz[u]-1)/2;
}
int get_lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    for(int i=19;i>=0;i--) if(deep[fa[x][i]]>=deep[y]) x=fa[x][i];
    if(x==y) return x;
    for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int aa[4000000];
bool cmp(int a,int b)
{
    return id[a]<id[b];
}
int my_stack[4000000];
int main()
{
    mempool[1].id=1;
    int n,m;
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    using namespace IStream;
    for(int i=n;i>=1;i--)
    {
        Insert(s[i]-'a');
        pos[i]=last->id;
    }
    for(int i=2;i<=now;i++)
        add_edge(mempool[i].parent->id,mempool[i].id);
    dfs(1,0);
    init();
    for(int hh=1;hh<=m;hh++)
    {
        T++;
        ans=0;
        tot=1;
        int nn;
        nn=get_int();
        for(int i=1;i<=nn;i++)
        {
            aa[i]=get_int();
            aa[i]=pos[aa[i]];
            pd[aa[i]]=T;
        }
        sort(aa+1,aa+1+nn,cmp);
        int top=1;
        my_stack[top]=1;
        for(int i=1;i<=nn;i++)
        {
            if(aa[i]==aa[i-1]) continue;
            int lca=get_lca(aa[i],my_stack[top]);
            while(1)
            {
                if(top>1 && deep[lca]<deep[my_stack[top-1]])
                {
                    add_edge(my_stack[top-1],my_stack[top]);
                    top--;
                }
                else if(deep[lca]<deep[my_stack[top]])
                {
                    add_edge(lca,my_stack[top]);
                    top--;
                    break;
                }
                else break;
            }
            if(my_stack[top]!=lca) my_stack[++top]=lca;
            my_stack[++top]=aa[i];
        }
        while(top>1)
        {
            add_edge(my_stack[top-1],my_stack[top]);
            top--;
        }
        dp(1,0);
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值