luoguP2178 [NOI2015]品酒大会(后缀数组做法)

题意

因为一个\(k\)相似必定为\(k-1,k-2....0\)相似,对于一个\(lcp\)\(k\)后缀对\((i,j)\),我们只用把它的贡献加在\(k\)的答案上,最后求一个后缀和和后缀max就可以得到答案。

考虑如何快速计算后缀对的贡献:
因为后缀对\((i,j),i>j\)\(lcp\)\(min_{k=i+1}^{j}height_k\),因此考虑将\(height\)从大到小排序。
对于当前的\(height_i\),我们找到\(sa_{i-1}\)\(sa_i\)所在后缀集合(一开始每个后缀是单独一个集合)。这时两个集合分别选两个后缀配对,\(lcp\)必定为\(height_i\),于是\(ans[height_i]\)就加上两集合大小的乘积,之后合并两个集合。第一问就做完了。

第二问只需要对每个集合维护最大值和最小值(负数乘负数会变成正数),合并时取个max即可。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3*1e5+10;
int n,num;
int fa[maxn],size[maxn];
ll a[maxn],maxx[maxn],minn[maxn],ans1[maxn],ans2[maxn];
char s[maxn];
struct node{int height,id;}h[maxn];
struct SA
{
	int num;
	int sa[maxn],rk[maxn],oldrk[maxn],id[maxn],tmpid[maxn],cnt[maxn],height[maxn];
	inline bool check(int x,int y,int k){return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];}
	inline void build(char* s,int len)
	{
		int num=300;
		for(int i=1;i<=len;i++)cnt[rk[i]=s[i]]++;
		for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
		for(int i=len;i;i--)sa[cnt[rk[i]]--]=i;
		for(int t=1;t<=len;t<<=1)
		{
			int tot=0;
			for(int i=len-t+1;i<=len;i++)id[++tot]=i;
			for(int i=1;i<=len;i++)if(sa[i]>t)id[++tot]=sa[i]-t;
			tot=0;
			memset(cnt,0,sizeof(cnt));
			for(int i=1;i<=len;i++)cnt[tmpid[i]=rk[id[i]]]++;
			for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
			for(int i=len;i;i--)sa[cnt[tmpid[i]]--]=id[i];
			memcpy(oldrk,rk,sizeof(rk));
			for(int i=1;i<=len;i++)rk[sa[i]]=check(sa[i-1],sa[i],t)?tot:++tot;
			num=tot;
			if(num==len)break;
		}
		for(int i=1,j=0;i<=len;i++)
		{
			if(j)j--;
			while(s[i+j]==s[sa[rk[i]-1]+j])j++;
			height[rk[i]]=j;
		}
	}
}Sa;
inline ll read()
{
	char c=getchar();ll res=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
	return res*f;
}
inline bool cmp(node x,node y){return x.height>y.height;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y,int k)
{
	int p=find(x),q=find(y);
	if(size[p]>size[q])swap(p,q);
	ans1[k]+=1ll*size[p]*size[q];
	ans2[k]=max(ans2[k],max(maxx[p]*maxx[q],minn[p]*minn[q]));
	minn[q]=min(minn[q],minn[p]);
	maxx[q]=max(maxx[q],maxx[p]);
	fa[p]=q;size[q]+=size[p];
}
int main()
{
	n=read();
	scanf("%s",s+1);
	Sa.build(s,n);
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)fa[i]=i,size[i]=1,maxx[i]=minn[i]=a[i];
	for(int i=2;i<=n;i++)h[i]=(node){Sa.height[i],i};
	sort(h+2,h+n+1,cmp);
	memset(ans2,-0x3f,sizeof(ans2));
	for(int i=2;i<=n;i++)merge(Sa.sa[h[i].id-1],Sa.sa[h[i].id],h[i].height);
	for(int i=n-1;~i;i--)ans1[i]+=ans1[i+1],ans2[i]=max(ans2[i],ans2[i+1]);
	for(int i=0;i<n;i++)
		if(ans1[i])printf("%lld %lld\n",ans1[i],ans2[i]);
		else puts("0 0");
	return 0;
}
posted @ 2019-12-17 14:29  nofind  阅读(139)  评论(0编辑  收藏  举报