[后缀数组][并查集]luogu P2178 [NOI2015] 品酒大会

题面

https://www.luogu.com.cn/problem/P2178

分析

对酒名用处理出height,按照height从大到小枚举(排除 1 ),由于 LCP(i,j)=min(LCP(k,k-1))(k>i) ,所以在 height 单调递减的情况下,可以用用并查集合并 i 和 i-1 ,则当前 height[i] 相似的字符串有 size[Father(i)]*size[Father(i-1)] 个

同时记录每个集合的最大最小值,再计算相互相乘的的结果记录到 height[i] 相似的最大美味值中

最后不要忘记 r 相似意味着 0~r-1 相似,所以方案数取后缀和,最大美味值取后缀最大值

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll Inf=1e18+1;
const int N=3e5+10;
int n;
int sa[N],rk[N],height[N],x[N],y[N],c[N];
int f[N],sz[N],ord[N];
ll sols[N],mxyum[N],ans[N],a[N],mx[N],mn[N];
char s[N];

int Get_F(int x) {return x==f[x]?x:f[x]=Get_F(f[x]);}

void Suffix_Array(int n,char *s,int *sa,int *rk) {
    int m='z',cnt;
    for (int i=0;i<=m;i++) c[i]=0;
    for (int i=1;i<=n;i++) c[x[i]=s[i]]++;
    for (int i=1;i<=m;i++) c[i]+=c[i-1];
    for (int i=n;i;i--) sa[c[x[i]]--]=i;
    for (int j=1;j<=n;j<<=1) {
        cnt=0;
        for (int i=n-j+1;i<=n;i++) y[++cnt]=i;
        for (int i=1;i<=n;i++) if (sa[i]>j) y[++cnt]=sa[i]-j;
        for (int i=0;i<=m;i++) c[i]=0;
        for (int i=1;i<=n;i++) c[x[i]]++;
        for (int i=1;i<=m;i++) c[i]+=c[i-1];
        for (int i=n;i;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);x[sa[1]]=cnt=1;
        for (int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[min(sa[i]+j,n+1)]==y[min(sa[i-1]+j,n+1)]?cnt:++cnt);
        m=cnt;if (m==n) break;
    }
    for (int i=1;i<=n;i++) rk[sa[i]]=i;
}

void Get_Height(int n,int *sa,int *rk,int *height) {
    int z=0;
    for (int i=1;i<=n;i++) {
        if (rk[i]==1) {height[rk[i]]=0;continue;}
        if (z) z--;
        while (i+z<=n&&sa[rk[i]-1]+z<=n&&s[i+z]==s[sa[rk[i]-1]+z]) z++;
        height[rk[i]]=z;
    }
}

bool CMP(int a,int b) {return height[a]>height[b];}

int main() {
    scanf("%d",&n);
    scanf("%s",s+1);
    Suffix_Array(n,s,sa,rk);Get_Height(n,sa,rk,height);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]),mx[i]=mn[i]=a[i],sz[f[i]=ord[i]=i]=1,mxyum[i]=ans[i]=-Inf;
    sort(ord+1,ord+n+1,CMP);
    for (int i=1,x,y;i<=n;i++)
        if (ord[i]!=1) {
            x=Get_F(sa[ord[i]]);y=Get_F(sa[ord[i]-1]);f[y]=x;
            sols[height[ord[i]]]+=1ll*sz[x]*sz[y];sz[x]+=sz[y];
            ans[height[ord[i]]]=max(ans[height[ord[i]]],mxyum[x]=max(max(mxyum[x],mxyum[y]),max(max(mx[x]*mx[y],mx[x]*mn[y]),max(mn[x]*mx[y],mn[x]*mn[y]))));
            mx[x]=max(mx[x],mx[y]);mn[x]=min(mn[x],mn[y]);
        }
    for (int i=n-1;~i;i--) sols[i]+=sols[i+1],ans[i]=max(ans[i],ans[i+1]);
    for (int i=0;i<n;i++) printf("%lld %lld
",sols[i],(sols[i]>0)*ans[i]);
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/14594261.html