[NOI2015] 品酒大会

[NOI2015] 品酒大会

重要性质

根据题意,如果两个字符串是r相似的,那么他们也是1相似,2相似...r-1相似的
也就是说,我们可以从大到小的考虑限制,预处理出两个后缀的最长前缀,先处理r相似的情况,再处理r-1相似的情况,再处理r-2相似的...
要求后缀的最长前缀,我们就可以联想到SA的性质,任意两个后缀串\(S_l,S_r\)之间的最长公共前缀为\(\underset{l<i \leq{r}}{min} ht_i\)
也就是如果我们把\(ht_i\)看作点i像点i-1连一条长度为\(ht_i\)的边,那么两个点之间的最长公共前缀就是他们之间所有连边的最小值
这样我们可以将边按ht排序,从大到小加边,每次加边会对\(ht_i\)的答案产生等同于两边连通块大小乘积的贡献并将两个连通块合并
这个过程显然可以用并查集维护
ACcode:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 400010
inline ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
struct node_dsu{
	ll f[MAXN],maxz[MAXN],minz[MAXN],siz[MAXN];
//	ll siz;
	void mk(ll a[],ll x){
		for(int i=1;i<=x;i++)maxz[i]=minz[i]=a[i],f[i]=i,siz[i]=1;
	}
	ll find(ll x){
		return f[x]==x?x:(f[x]=find(f[x]));
	}
	pair<ll,ll> merge(ll x,ll y){
		x=find(x),y=find(y);
		ll sum=(siz[x])*(siz[y]),ans=max(max(maxz[x]*maxz[y],minz[x]*minz[y]),max(minz[x]*maxz[y],maxz[x]*minz[y]));
		f[x]=y;
		siz[y]+=siz[x];
		maxz[y]=max(maxz[y],maxz[x]);
		minz[y]=min(minz[y],minz[x]);
//		maxz[y]=max()
		return make_pair(sum,ans);
	}
}DSU;
struct node_SA{
	char c[MAXN];
	ll n;
	ll rk[MAXN],_rk[MAXN],sa[MAXN],ht[MAXN];
	ll id[MAXN],_id[MAXN],it[MAXN];
	void getin(ll x){
		n=x;
		for(int i=1;i<=n;i++)cin>>c[i];
	}
	bool cmp(ll a,ll b,ll w){
		return _rk[a]==_rk[b]&&_rk[a+w]==_rk[b+w];
	}
	void build(){
		ll m=26,p=0;
		for(int i=1;i<=n;i++)it[rk[i]=c[i]-'a'+1]++;
		for(int i=1;i<=m;i++)it[i]+=it[i-1];
		for(int i=1;i<=n;i++)sa[it[rk[i]]--]=i;
		for(int w=1;;w<<=1,m=p,p=0){
			for(int i=n;i>n-w;i--)id[++p]=i;
			for(int i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
			for(int i=1;i<=m;i++)it[i]=0;
			for(int i=1;i<=n;i++)it[_id[i]=rk[id[i]]]++;
			for(int i=1;i<=m;i++)it[i]+=it[i-1];
			for(int i=n;i;i--)sa[it[_id[i]]--]=id[i];
			for(int i=1;i<=n;i++)_rk[i]=rk[i],rk[i]=0;
			p=0;
			for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
			if(p>=n)break;
		}
		ll k=0;
		for(int i=1;i<=n;i++){
			if(k)k--;
			while(c[i+k]==c[sa[rk[i]-1]+k])k++;
			ht[rk[i]]=k;
		}
//		for(int i=1;i<=n;i++)cout<<rk[i]<<" ";
//		cout<<endl;
//		for(int i=1;i<=n;i++)cout<<sa[i]<<" ";
//		cout<<endl;
//cout<<"ht ";
//		for(int i=1;i<=n;i++)cout<<ht[i]<<" ";
//		cout<<endl;
	}
}SA;
ll n,a[MAXN];
pair<ll,ll> s[MAXN];
ll sum[MAXN],ans[MAXN];
int main(){
	cin>>n;
	SA.getin(n);
	SA.build();
	for(int i=1;i<=n;i++)cin>>a[SA.rk[i]];
	DSU.mk(a,n);
//	for(int i=1;i<=n;i++)cout<<a[i]<<" ";
//	cout<<endl;
	for(int i=2;i<=n;i++){
		s[i].first=SA.ht[i],s[i].second=i;
	}
	sort(s+1,s+1+n);
	ll it=n;
//	for(int i=1;i<=n;i++)cout<<s[i].second<<" "<<s[i].first<<endl;
	ans[n]=-1e9;
	for(int i=n-1;i>=0;i--){
		sum[i]=sum[i+1],ans[i]=ans[i+1];
		while(it){
			if(s[it].first>=i){
//				cout<<"it: "<<it<<" pos: "<<s[it].second<<" val: "<<s[it].first<<endl;
				pair<ll,ll> p=DSU.merge(s[it].second-1,s[it].second);
				sum[i]+=p.first,ans[i]=max(ans[i],p.second);
				it--;
			}else break;
		}
//		cout<<sum<<" "<<ans<<endl;
	}
	for(int i=0;i<=n-1;i++)cout<<sum[i]<<" "<<((ans[i]==-1e9)?0:ans[i])<<endl;
	return 0;
}

posted @ 2024-12-19 21:49  flyfreemrn  阅读(10)  评论(0)    收藏  举报