[NOI2015]品酒大会(后缀树+DP)

后缀自动机有一个性质。就是两个串的lcp就是这两个点的结束节点的LCA对应前缀。
然后就可以愉快的跑DP了。
对于每一个后缀树上的节点\(u\),它对\(len[u]\)的贡献是\(\sum_{v1}\sum_{v2\neq{v1}}size[v1]*size[v2]\)当然如果u就是一个后缀的结尾就要加上自己。
然后最大值怎么办?我们在每一个节点上维护最小次小,最大次大然后DP转移就行。
因为这题\(k\)相似就是\(k-1\)相似。最后还要对个数求个和,对最大值求一个\(max\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define int long long
const int N=601000;
const int INF=1e18+10;
int cnt,head[N];
int tot=1,u=1,trans[N][27],size[N],len[N],fa[N];
int mx[N],mxx[N],mn[N],mnn[N],dp[N];
int ans[N],anss[N],n,a[N];
char s[N];
struct edge{
    int to,nxt;
}e[N];
void add(int u,int v){
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'||ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
void ins(int id,int c){
    int x=++tot;len[x]=len[u]+1;
    mx[x]=mn[x]=a[id];size[x]=1;mxx[x]=-INF;mnn[x]=INF;
    for(;u&&trans[u][c]==0;u=fa[u])trans[u][c]=x;
    if(u==0)fa[x]=1;
    else{
        int v=trans[u][c];
        if(len[u]+1==len[v])fa[x]=v;
        else{
            int w=++tot;len[w]=len[u]+1;
            mx[w]=-INF;mn[w]=INF;mxx[x]=-INF;mnn[x]=INF;
            memcpy(trans[w],trans[v],sizeof(trans[w]));
            fa[w]=fa[v];
            fa[x]=fa[v]=w;
            for(;u&&trans[u][c]==v;u=fa[u])trans[u][c]=w;
        }
    }
    u=x;
}
void dfs(int u){
    int tmp=size[u];
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        dfs(v);
        dp[u]+=size[v]*tmp;
        tmp+=size[v];
        if(mx[v]>mx[u]){
            mxx[u]=mx[u];
            mx[u]=mx[v];
            if(mxx[v]>mxx[u])mxx[u]=mxx[v];
        }
        else if(mx[v]>mxx[u])mxx[u]=mx[v];
        if(mn[v]<mn[u]){
            mnn[u]=mn[u];
            mn[u]=mn[v];
            if(mnn[v]<mnn[u])mnn[u]=mnn[v];
        }
        else if(mn[v]<mnn[u])mnn[u]=mn[v];
        size[u]+=size[v];
    }
    ans[len[u]]+=dp[u];
    if(size[u]!=1)anss[len[u]]=max(anss[len[u]],max(mx[u]*mxx[u],mn[u]*mnn[u]));
}
signed main(){
    scanf("%lld",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=n;i>=1;i--)ins(i,s[i]-'a'+1);
    for(int i=1;i<=tot;i++)add(fa[i],i);
    for(int i=0;i<=n+1;i++)anss[i]=-INF;
    dfs(1);
    for(int i=n;i>=0;i--)ans[i]+=ans[i+1],anss[i]=max(anss[i],anss[i+1]);
    for(int i=0;i<n;i++)
        if(ans[i]==0)printf("0 0\n");
        else printf("%lld %lld\n",ans[i],anss[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Xu-daxia/p/10212256.html