P2178 [NOI2015] 品酒大会 题解

P2178 [NOI2015] 品酒大会

题面啰唆,实际上所谓 “\(p\)\(q\)\(r\) 相似” 的意思就是以 \(p\) 开头的后缀和以 \(q\) 开头的后缀的前 \(r\) 个字符都是相等的。所以,\(\operatorname{LCP}(p,q)=r\) 实际上等价于 \(p\)\(q\)\(1\) 相似、\(2\) 相似、……一直到 \(r\) 相似。

那这个问题就是后缀数组的典型应用:求后缀 LCP 所带来的贡献,在此之前同类型的一道题是 P4248 [AHOI2013] 差异。求出 \(\rm sa\)\(\rm height\) 后,我们采用单调栈求出向左和向右第一个小于 \(\text{height}(i)\)\(\text{height}(j)\) 的位置分别记作 \(l_i\)\(r_i\),那么 \((l_i,r_i)\) 这段区间就是 \(i\) 作为区间最小值的区间,也就是它能提供贡献的区间。那么第一问的答案就是 \((i-l_i)\times(r_i-i)\),这和上面那道题是一样的。然后考虑求解第二问,同理因为提供贡献的区间还是在 \((l_i,r_i)\) 这个范围内,所以要用 ST 表维护区间最大值和最小值(有可能负负得正),就得到当前点第二问的答案。

特殊地,因为题目中的定义,\(i\) 相似的答案同时也是 \(i-1\) 相似的答案,所以需要从后往前将第一问答案求和,第二问答案求最大值,然后才能输出。

#include<bits/stdc++.h>
using namespace std;

using ll=long long;
constexpr int MAXN=3e5+5;
constexpr ll INF=0xc0c0c0c0c0c0c0c0ll;
int n;
ll a[MAXN];
string s;
int sa[MAXN],rk[MAXN],rk2[MAXN],id[MAXN],cnt[MAXN];
int h[MAXN],f[MAXN][21],g[MAXN][21];
ll ans[MAXN],res[MAXN];
int stk[MAXN],top,l[MAXN],r[MAXN];

void getsa(int m){
	for(int i=1;i<=n;i++) cnt[rk[i]=s[i]]++;
	for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i;i--) sa[cnt[rk[i]]--]=i;
	for(int w=1,p,cur;;w<<=1,m=p){
		cur=0;
		for(int i=n-w+1;i<=n;i++) id[++cur]=i;
		for(int i=1;i<=n;i++) if(sa[i]>w) id[++cur]=sa[i]-w;
		memset(cnt,0,(m+1)<<2);
		for(int i=1;i<=n;i++) cnt[rk[i]]++;
		for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i;i--) sa[cnt[rk[id[i]]]--]=id[i];
		p=0;
		memcpy(rk2,rk,(n+1)<<2);
		for(int i=1;i<=n;i++)
			rk[sa[i]]=rk2[sa[i]]==rk2[sa[i-1]]&&rk2[sa[i]+w]==rk2[sa[i-1]+w]?p:++p;
		if(p==n) break;
	}
}
void geth(){
	for(int i=1,k=0;i<=n;i++){
		if(!rk[i]) continue;
		if(k) k--;
		while(s[i+k]==s[sa[rk[i]-1]+k]) k++;
		h[rk[i]]=k;
	}
}
void ST(){
	for(int i=1;i<=n;i++) f[i][0]=g[i][0]=a[sa[i]];
	for(int j=1;j<=20;j++)
		for(int i=1;i+(1<<j)-1<=n;i++){
			f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			g[i][j]=max(g[i][j-1],g[i+(1<<(j-1))][j-1]);
		}
}
ll amn(int l,int r){
	int s=__lg(r-l+1);
	return min(f[l][s],f[r-(1<<s)+1][s]);
}
ll amx(int l,int r){
	int s=__lg(r-l+1);
	return max(g[l][s],g[r-(1<<s)+1][s]);
}

int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin>>n>>s;
	s=' '+s;
	getsa('z'),geth();
	for(int i=1;i<=n;i++) cin>>a[i];
	ST();
	memset(res,0xc0,(n+1)<<3);
	for(int i=1;i<=n;i++){
		while(top&&h[stk[top]]>h[i]) top--;
		l[i]=stk[top];
		stk[++top]=i;
	}
	stk[top=1]=n+1;
	h[n+1]=-1;
	for(int i=n;i;i--){
		while(top&&h[stk[top]]>=h[i]) top--;
		r[i]=stk[top]; 
		stk[++top]=i;
	}
	for(int i=2;i<=n;i++){
		ans[h[i]]+=1ll*(i-l[i])*(r[i]-i);
		res[h[i]]=max({res[h[i]],amn(l[i],i-1)*amn(i,r[i]-1),amx(l[i],i-1)*amx(i,r[i]-1)});
	}
	for(int i=n-2;~i;i--){
		ans[i]+=ans[i+1];
		res[i]=max(res[i],res[i+1]);
	}
	for(int i=0;i<n;i++) cout<<ans[i]<<' '<<(res[i]==INF?0:res[i])<<'\n';
	return 0;
}
posted @ 2025-02-23 20:39  Laoshan_PLUS  阅读(33)  评论(0)    收藏  举报