BZOJ4199[Noi2015]品酒大会 后缀数组+并查集
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4199[Noi2015]品酒大会 后缀数组+并查集相关的知识,希望对你有一定的参考价值。
【BZOJ4199】[Noi2015]品酒大会
题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144
题解:听说能用SAM?SA默默水过~
本题的实现还是非常简单的,先求出height数组,然后两杯酒‘r‘相似就等价于二者中间的height都>=r,于是我们将height排序,从大到小扔进去,那么所有连续的区间中的点对就都是相似的了。维护连续区间可以用并查集。统计点对个数需要维护size,统计最大乘积需要维护最(次)大(小)值,然后没了~
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn=300010; int n,m; typedef long long ll; int sa[maxn],st[maxn],ra[maxn],rb[maxn],r[maxn],h[maxn],rank[maxn]; int f[maxn],q[maxn],v[maxn]; ll ans[maxn],sum[maxn],m1[maxn],m2[maxn],n1[maxn],n2[maxn],siz[maxn]; char str[maxn]; void work() { int i,j,p,k,*x=ra,*y=rb; for(i=0;i<n;i++) st[x[i]=r[i]]++; for(i=1;i<m;i++) st[i]+=st[i-1]; for(i=n-1;i>=0;i--) sa[--st[x[i]]]=i; for(j=p=1;p<n;j<<=1,m=p) { for(p=0,i=n-j;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; for(i=0;i<m;i++) st[i]=0; for(i=0;i<n;i++) st[x[y[i]]]++; for(i=1;i<m;i++) st[i]+=st[i-1]; for(i=n-1;i>=0;i--) sa[--st[x[y[i]]]]=y[i]; for(swap(x,y),x[sa[0]]=0,i=p=1;i<n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?p-1:p++; } for(i=1;i<n;i++) rank[sa[i]]=i; for(i=k=0;i<n-1;h[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); } int find(int x) { return (f[x]==x)?x:(f[x]=find(f[x])); } bool cmp(int a,int b) { return h[a]>h[b]; } int main() { scanf("%d%s",&n,str); int i,j,a,b; for(i=0;i<n;i++) r[i]=str[i]-‘a‘+1; for(i=0;i<n;i++) scanf("%d",&v[i]); m=27,n++,work(),n--; for(i=1;i<=n;i++) q[i]=f[i]=i,m1[i]=n1[i]=v[sa[i]],m2[i]=-1ll<<60,n2[i]=1ll<<60,siz[i]=1; sort(q+2,q+n+1,cmp); ans[n]=-1ll<<60; for(i=2,j=n-1;j>=0;j--) { ans[j]=ans[j+1],sum[j]=sum[j+1]; for(;h[q[i]]==j&&i<=n;i++) { a=find(q[i]-1),b=find(q[i]); if(m1[a]>m1[b]) m2[b]=max(m1[b],m2[a]),m1[b]=m1[a]; else m2[b]=max(m2[b],m1[a]); if(n1[a]<n1[b]) n2[b]=min(n1[b],n2[a]),n1[b]=n1[a]; else n2[b]=min(n2[b],n1[a]); ans[j]=max(ans[j],max(n1[b]*n2[b],m1[b]*m2[b])); sum[j]+=siz[a]*siz[b]; siz[b]+=siz[a],f[a]=b; } } for(i=0;i<n;i++) printf("%lld %lld\n",sum[i],ans[i]==ans[n]?0:ans[i]); return 0; }
以上是关于BZOJ4199[Noi2015]品酒大会 后缀数组+并查集的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ4199[Noi2015]品酒大会 后缀数组+并查集