luoguP2178 [NOI2015]品酒大会(后缀自动机)

题意

承接上篇题解

考虑两个后缀的\(lcp\)是什么,是将串反着插入后缀自动机后两个前缀(终止节点)的\(lca\)!!!于是可以在parent tree上DP了。

比后缀数组又简单又好写跑的还快。

code:

#include
using namespace std;
typedef long long ll;
const int maxn=3*1e5+10;
int n,last,tot,cnt;
int head[maxn<<1],size[maxn<<1];
ll inf;
ll a[maxn],val[maxn<<1],maxx[maxn<<1],minn[maxn<<1],ans1[maxn],ans2[maxn];
char s[maxn];
struct edge{int to,nxt;}e[maxn<<1];
struct SAM
{
    int fa,len;
    int ch[30];
}sam[maxn<<1];
inline ll read()
{
    char c=getchar();ll res=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
    return res*f;
}
inline void add(int u,int v){e[++cnt].nxt=head[u];head[u]=cnt;e[cnt].to=v;}
inline void sam_init(){sam[0].fa=-1;sam[0].len=0;last=0;}
inline void sam_add(int c,int id)
{
    int now=++tot;sam[now].len=sam[last].len+1;size[now]=1;val[now]=a[id];
    int p=last;last=now;
    while(~p&&!sam[p].ch[c])sam[p].ch[c]=now,p=sam[p].fa;
    if(p==-1){sam[now].fa=0;return;}
    int q=sam[p].ch[c];
    if(sam[q].len==sam[p].len+1)sam[now].fa=q;
    else
    {
        int nowq=++tot;
        sam[nowq].len=sam[p].len+1;
        memcpy(sam[nowq].ch,sam[q].ch,sizeof(sam[q].ch));
        sam[nowq].fa=sam[q].fa,sam[q].fa=sam[now].fa=nowq;
        while(~p&&sam[p].ch[c]==q)sam[p].ch[c]=nowq,p=sam[p].fa;
    }
}
void dfs(int x)
{
    maxx[x]=-inf,minn[x]=inf;
    if(size[x])maxx[x]=minn[x]=val[x];
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        dfs(y);
        if(minn[x]!=inf&&minn[y]!=inf&&maxx[x]!=-inf&&maxx[x]!=-inf)
            ans2[sam[x].len]=max(ans2[sam[x].len],max(maxx[x]*maxx[y],minn[x]*minn[y]));
        maxx[x]=max(maxx[x],maxx[y]),minn[x]=min(minn[x],minn[y]);
        ans1[sam[x].len]+=1ll*size[x]*size[y];
        size[x]+=size[y];
    }
}
int main()
{
    n=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)a[i]=read();
    sam_init();
    for(int i=n;i;i--)sam_add(s[i]-'a',i);
    for(int i=1;i<=tot;i++)add(sam[i].fa,i);
    memset(ans2,-0x3f,sizeof(ans2));inf=-ans2[0];
    dfs(0);
    for(int i=n-1;~i;i--)ans1[i]+=ans1[i+1],ans2[i]=max(ans2[i],ans2[i+1]);
    for(int i=0;i

你可能感兴趣的:(luoguP2178 [NOI2015]品酒大会(后缀自动机))