zoukankan      html  css  js  c++  java
  • 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
    ",ans1[i],ans2[i]);
    		else puts("0 0");
    	return 0;
    }
    
  • 相关阅读:
    BZOJ1257:[CQOI2007]余数之和——题解+证明
    BZOJ3781:小B的询问——题解
    BZOJ2038:[2009国家集训队]小Z的袜子——题解
    BZOJ3052 & UOJ58:[WC2013]糖果公园——题解
    BZOJ1086:[SCOI2005]王室联邦——题解
    BZOJ1878:[SDOI2009]HH的项链——题解
    BZOJ2453:维护队列——题解
    美团新零售招聘-高级测试开发(20k-50k/月)
    Shopee招聘-测试开发leader(30k-60k/月)
    蚂蚁金服招聘-无线测试开发(20k-36k/月)
  • 原文地址:https://www.cnblogs.com/nofind/p/12054360.html
Copyright © 2011-2022 走看看