bzoj4199[luoguP2178]品酒大会[noi2015] (后缀数组+并查集)

bzoj上居然没题面。。可怕。洛谷上都有。首先,r相似其实就是lcp为r。那么就有个显然的做法。每次枚举lcp,按h分组,一组内有cnt个,则答案就是在cnt个数中选两个,C(cnt 2)。符号不会打,意思到了就好。。然后最大值。。想想怎么维护就好了。直接看代码吧。然后就可以拿到50分。

而正解是这样的:r相似是不是至少有r+1相似个?那么r+1相似有多少个是不是跟r相似没啥关系??所以我们可以把所有的h从大到小排序,按顺序做。ans[i]=ans[i+1].而这一次答案可能会更多,来源于h[p]==i的两个串。我们用并查集维护一下。每次多的其实就是两个块的两两匹配,即size[x]*size[y].做完我们就把这两个串合起来。。至于最大值怎么更新。见代码吧。

其实还可以这样理解下:我们以前做的按h分组,是不是h越大,分组越散?h逐渐变小,一个个分组就连起来了??像不像并查集!!!
 
50分做法:
#include <cstdio>
#include <cstring>
#define ll long long
#define N 300010
#define inf 1000000000000000001;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}
inline ll max(ll x,ll y){return x>y?x:y;}
int n,m,a[N],rank[N<<1],rank1[N],count[N],sa[N],h[N],tmp[N];
char s[N];
inline bool solve(int len){
	ll cnt=0,ans=0,mx=-inf;
	int a1=0,a2=0,b1=0,b2=0;//a>0,b<0
	for(int i=1;i<=n+1;++i){
		if(h[i]>=len){
			++cnt;if(a[sa[i]]>a1) a2=a1,a1=a[sa[i]];
			else if(a[sa[i]]>a2) a2=a[sa[i]];
			else if(a[sa[i]]<b1) b2=b1,b1=a[sa[i]];
			else if(a[sa[i]]<b2) b2=a[sa[i]];
		}
		else{
			if(cnt>=2){
				ans+=(ll)cnt*(cnt-1)/2;
				if(a2==0&&b2==0) mx=max(mx,(ll)a1*b1);
				else if(a2==0) mx=max(mx,(ll)b1*b2);
				else if(b2==0) mx=max(mx,(ll)a1*a2);
				else mx=max(mx,max((ll)a1*a2,(ll)b1*b2));
			}
			cnt=1;a1=a2=b1=b2=0;
			if(a[sa[i]]>0) a1=a[sa[i]];else b1=a[sa[i]];
		}
	}
	if(ans==0){printf("0 0\n");return 0;}
	else{
		printf("%lld %lld\n",ans,mx);return 1;
	}
}
int main(){
	freopen("a.in","r",stdin);
	n=read();scanf("%s",s+1);for(int i=1;i<=n;++i) a[i]=read();
	m='z'+1;
	for(int i=1;i<=m;++i) count[i]=0;
	for(int i=1;i<=n;++i) count[s[i]]=1;
	for(int i=1;i<=m;++i) count[i]+=count[i-1];
	for(int i=n;i>=1;--i) rank[i]=count[s[i]];
	int k=0;m=27;
	for(int p=1;k!=n;p<<=1,m=k){
		for(int i=1;i<=m;++i) count[i]=0;
		for(int i=1;i<=n;++i) count[rank[i+p]]++;
		for(int i=1;i<=m;++i) count[i]+=count[i-1];
		for(int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
		for(int i=1;i<=m;++i) count[i]=0;
		for(int i=1;i<=n;++i) count[rank[tmp[i]]]++;
		for(int i=1;i<=m;++i) count[i]+=count[i-1];
		for(int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
		memcpy(rank1,rank,sizeof(rank1));
		rank[sa[1]]=k=1;
		for(int i=2;i<=n;++i){
			if(rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;
			rank[sa[i]]=k;
		}
	}k=0;
	for(int i=1;i<=n;++i){
		if(rank[i]==1){h[1]=0;continue;}
		if(i==1||h[rank[i-1]]<=1) k=0;
		if(k) --k;
		while(s[i+k]==s[sa[rank[i]-1]+k]) ++k;
		h[rank[i]]=k;
	}h[n+1]=-1;
	for(int i=0;i<n;++i){
		if(!solve(i)){
			for(int j=i+1;j<n;++j) printf("0 0\n");break;
		}
	}
	return 0;
}
正解:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define N 300010
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}
template <typename T>
inline T max(T x,T y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
struct node{
	int h,x,y;
}data[N];
int n,m,a[N],rank[N<<1],rank1[N],count[N],sa[N],h[N],tmp[N];
int fa[N],size[N],mx[N],mn[N];
ll ans[N],ansmx[N];
char s[N];
inline bool cmp(node x,node y){return x.h>y.h;}
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){
	fa[y]=x;size[x]+=size[y];
	mx[x]=max(mx[x],mx[y]);
	mn[x]=min(mn[x],mn[y]);
}
int main(){
//	freopen("a.in","r",stdin);
	n=read();scanf("%s",s+1);for(int i=1;i<=n;++i) a[i]=read();
	m='z'+1;
	for(int i=1;i<=m;++i) count[i]=0;
	for(int i=1;i<=n;++i) count[s[i]]=1;
	for(int i=1;i<=m;++i) count[i]+=count[i-1];
	for(int i=n;i>=1;--i) rank[i]=count[s[i]];
	int k=0;m=27;
	for(int p=1;k!=n;p<<=1,m=k){
		for(int i=1;i<=m;++i) count[i]=0;
		for(int i=1;i<=n;++i) count[rank[i+p]]++;
		for(int i=1;i<=m;++i) count[i]+=count[i-1];
		for(int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
		for(int i=1;i<=m;++i) count[i]=0;
		for(int i=1;i<=n;++i) count[rank[tmp[i]]]++;
		for(int i=1;i<=m;++i) count[i]+=count[i-1];
		for(int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
		memcpy(rank1,rank,sizeof(rank1));
		rank[sa[1]]=k=1;
		for(int i=2;i<=n;++i){
			if(rank1[sa[i]]!=rank1[sa[i-1]]||rank1[sa[i]+p]!=rank1[sa[i-1]+p]) ++k;
			rank[sa[i]]=k;
		}
	}k=0;
	for(int i=1;i<=n;++i){
		if(rank[i]==1){h[1]=0;continue;}
		if(i==1||h[rank[i-1]]<=1) k=0;
		if(k) --k;
		while(s[i+k]==s[sa[rank[i]-1]+k]) ++k;
		h[rank[i]]=k;
	}
	memset(ansmx,128,sizeof(ansmx));
	for(int i=1;i<=n;++i) fa[i]=i,size[i]=1,mx[i]=mn[i]=a[sa[i]];
	for(int i=1;i<n;++i) data[i].h=h[i+1],data[i].x=i,data[i].y=i+1;
	std::sort(data+1,data+n,cmp);
	for(int i=data[1].h,p=1;i>=0;--i){
		ans[i]=ans[i+1];ansmx[i]=ansmx[i+1];
		while(p<=n-1&&data[p].h==i){
			int x=find(data[p].x),y=find(data[p].y);
			ans[i]+=(ll)size[x]*size[y];
			ansmx[i]=max(ansmx[i],(ll)mx[x]*mx[y]);
			ansmx[i]=max(ansmx[i],(ll)mn[x]*mn[y]);
			merge(x,y);++p;
		}
	}
	for(int i=0;i<n;++i){
		printf("%lld %lld\n",ans[i],ans[i]==0?0:ansmx[i]);
	}
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值