P2178 [NOI2015]品酒大会

思路

在后缀树上进行一些操作就好了
后缀树上LCA的maxlen就是两个后缀的LCP的长度了
然后统计每个点作为LCA的次数和最大值、次大值、最小值、次小值
然后就做完了

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
int val[301000*2],n;
char s[301000*2];
namespace SAM{
    int maxlen[301000*2],suflink[301000*2],trans[301000*2][26],endpos[301000*2],maxnum[301000*2],semaxnum[301000*2],minnum[301000*2],seminnum[301000*2],Nodecnt=1,in[301000*2],num[301000*2],maxval[301000*2];
    int fir[301000*2],v[301000*2],nxt[301000*2],cnt=0;
    void addedge(int ui,int vi){
        ++cnt;
        v[cnt]=vi;
        nxt[cnt]=fir[ui];
        fir[ui]=cnt;
    }   
    int New_state(int _suflink,int *_trans,int _maxlen){
        ++Nodecnt;
        suflink[Nodecnt]=_suflink;
        if(_trans)
            for(int i=0;i<26;i++)
                trans[Nodecnt][i]=_trans[i];
        maxlen[Nodecnt]=_maxlen;                
        return Nodecnt;
    }
    int add_len(int u,int c,int inq){
        int z=New_state(0,NULL,maxlen[u]+1);
        endpos[z]=1;
        maxnum[z]=minnum[z]=inq;
        semaxnum[z]=-0x3f3f3f3f;
        seminnum[z]=0x3f3f3f3f;
        while(u&&trans[u][c]==0){
            trans[u][c]=z;
            u=suflink[u];
        }
        if(!u){
            suflink[z]=1;
            return z;
        }
        int v=trans[u][c];
        if(maxlen[v]==maxlen[u]+1){
            suflink[z]=v;
            return z;
        }
        int y=New_state(suflink[v],trans[v],maxlen[u]+1);
        endpos[y]=0;
        maxnum[y]=semaxnum[y]=-0x3f3f3f3f;
        minnum[y]=seminnum[y]=0x3f3f3f3f;
        suflink[z]=suflink[v]=y;
        while(u&&trans[u][c]==v){
            trans[u][c]=y;
            u=suflink[u];
        }
        return z;
    }
    void insert(char *s,int len){
        reverse(s+1,s+len+1);
        reverse(val+1,val+len+1);
        int last=1;
        for(int i=1;i<=len;i++)
            last=add_len(last,s[i]-'a',val[i]);
    }
    void build(void){
        memset(num,0,sizeof(num));
        memset(maxval,-0x3f,sizeof(maxval));
        for(int i=1;i<=Nodecnt;i++)
            addedge(suflink[i],i);
    }
    void dfs(int u){
        for(int i=fir[u];i;i=nxt[i]){
            dfs(v[i]);
            num[maxlen[u]]+=endpos[u]*endpos[v[i]];
            endpos[u]+=endpos[v[i]];
            if(maxnum[v[i]]>=maxnum[u]){
                if(max(maxnum[u],semaxnum[v[i]])>semaxnum[u])
                    semaxnum[u]=max(maxnum[u],semaxnum[v[i]]);
                maxnum[u]=maxnum[v[i]];
            }
            else if(maxnum[v[i]]>semaxnum[u]){
                semaxnum[u]=maxnum[v[i]];
            }
            if(minnum[v[i]]<=minnum[u]){
                if(min(seminnum[v[i]],minnum[u])<seminnum[u])
                    seminnum[u]=min(seminnum[v[i]],minnum[u]);
                minnum[u]=minnum[v[i]];
            }
            else if(minnum[v[i]]<seminnum[u]){
                seminnum[u]=minnum[v[i]];
            }
        }
        if(endpos[u]>1)
            maxval[maxlen[u]]=max(maxval[maxlen[u]],max(maxnum[u]*semaxnum[u],minnum[u]*seminnum[u]));
    }
};
signed main(){
    scanf("%lld",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        scanf("%lld",&val[i]);
    SAM::insert(s,n);
    SAM::build();
    SAM::dfs(1);
    for(int i=n;i>=0;i--){
        SAM::maxval[i]=max(SAM::maxval[i],SAM::maxval[i+1]);
        SAM::num[i]+=SAM::num[i+1];
    }
    for(int i=0;i<n;i++)
        printf("%lld %lld\n",SAM::num[i],(SAM::num[i]==0)?0:SAM::maxval[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/dreagonm/p/10745713.html