bzoj 4199: [Noi2015]品酒大会 后缀数组+并查集

来源:互联网 发布:淘宝卖会员账号 编辑:程序博客网 时间:2024/06/05 17:59

题意

给出一个 长度为 n 的字符串,每一位有一个权值 val。定义两个位字符为 r 相似,是指分别从这两个字符开始,到后面的 r 个字符都相等。两个 r 相似的字符还有一个权值为这两个字符权值的乘积。问对于 r = 0, 1, 2, … , n - 1,统计出有多少种方法可以选出 2 个“r 相似”的字符,并回答选择 2 个”r 相似”的字符可以得到的权值的最大值。
n<=3*10^5,val <=10^9

分析

先构造出height数组,然后把height数组从大到小排序,并逐个加入。每加入一个就用并查集维护一下数量,最大值次大值,最小值次小值并计算一波贡献即可。

一开始用vector来排序height数组结果T了,改成排序才过。。。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=300005;const int inf=0x7fffffff;int n,b[N],c[N],d[N],rank[N*2],sa[N],height[N],f[N],size[N],mx1[N],mx2[N],mn1[N],mn2[N],a[N],num[N];LL ans[N],mx[N];char s[N];void get_sa(int n,int m){    for (int i=1;i<=n;i++) b[s[i]]++;    for (int i=1;i<=m;i++) b[i]+=b[i-1];    for (int i=n;i>=1;i--) c[b[s[i]]--]=i;    int t=0;    for (int i=1;i<=n;i++)    {        if (s[c[i]]!=s[c[i-1]]) t++;        rank[c[i]]=t;    }    int j=1;    while (j<=n)    {        for (int i=1;i<=n;i++) b[i]=0;        for (int i=1;i<=n;i++) b[rank[i+j]]++;        for (int i=1;i<=n;i++) b[i]+=b[i-1];        for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;        for (int i=1;i<=n;i++) b[i]=0;        for (int i=1;i<=n;i++) b[rank[i]]++;        for (int i=1;i<=n;i++) b[i]+=b[i-1];        for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];        t=0;        for (int i=1;i<=n;i++)        {            if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++;            c[d[i]]=t;        }        for (int i=1;i<=n;i++) rank[i]=c[i];        if (t==n) break;        j*=2;    }    for (int i=1;i<=n;i++) sa[rank[i]]=i;}void get_height(int n){    int k=0;    for (int i=1;i<=n;i++)    {        if (k) k--;        int j=sa[rank[i]-1];        while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;        height[rank[i]]=k;    }}int find(int x){    if (f[x]==x) return x;    f[x]=find(f[x]);    return f[x];}LL calc(int x){    return (LL)size[x]*(size[x]-1)/2;}void merge(int x,int y){    size[y]+=size[x];f[x]=y;    if (mx1[x]!=-inf)    {        int w=mx1[x];        if (w>mx1[y]) mx2[y]=mx1[y],mx1[y]=w;        else if (w>mx2[y]) mx2[y]=w;    }    if (mx2[x]!=-inf)    {        int w=mx2[x];        if (w>mx1[y]) mx2[y]=mx1[y],mx1[y]=w;        else if (w>mx2[y]) mx2[y]=w;    }    if (mn1[x]!=inf)    {        int w=mn1[x];        if (w<mn1[y]) mn2[y]=mn1[y],mn1[y]=w;        else if (w<mn2[y]) mn2[y]=w;    }    if (mn2[x]!=inf)    {        int w=mn2[x];        if (w<mn1[y]) mn2[y]=mn1[y],mn1[y]=w;        else if (w<mn2[y]) mn2[y]=w;    }}bool cmp(int a,int b){    return height[a]>height[b];}int main(){    scanf("%d",&n);    scanf("%s",s+1);    for (int i=1;i<=n;i++) scanf("%d",&a[i]);    get_sa(n,200);    get_height(n);    for (int i=1;i<n;i++) num[i]=i+1;    sort(num+1,num+n,cmp);    for (int i=1;i<=n;i++) f[i]=i,mn1[i]=mx1[i]=a[sa[i]],mn2[i]=inf,mx2[i]=-inf,size[i]=1;    for (int i=0;i<=n;i++) mx[i]=-(LL)inf*inf;    int p=1;    for (int i=n-1;i>=0;i--)    {        ans[i]=ans[i+1];mx[i]=mx[i+1];        while (height[num[p]]==i&&p<n)        {            int x=num[p];            ans[i]-=calc(find(x))+calc(find(x-1));            merge(find(x),find(x-1));            int w=find(x);            ans[i]+=calc(w);            mx[i]=max(mx[i],max((LL)mx1[w]*mx2[w],(LL)mn1[w]*mn2[w]));            p++;        }    }    for (int i=0;i<=n-1;i++) printf("%lld %lld\n",ans[i],mx[i]==-(LL)inf*inf?0:mx[i]);    return 0;}
0 0