[NOI2015]品酒大会(后缀树+DP)

后缀自动机有一个性质。
就是如果倒建SAM两个串的lcp就是这两个串的结束节点的LCA。
然后就可以愉快的跑DP了。
对于每一个后缀树上的节点\(u\),它对\(len[u]\)的贡献是\(\sum_{v1}\sum_{v2\neq{v1}}size[v1]*size[v2]\)当然如果u就是一个后缀的结尾就要加上自己。
然后最大值怎么办?我们在每一个节点上维护最小次小,最大次大然后DP转移就行。
因为这题\(k\)相似就是\(k-1\)相似。最后还要对个数求个和,对最大值求一个\(max\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define int long long
const int N=601000;
const int INF=1e18+10;
int cnt,head[N];
int tot=1,u=1,trans[N][27],size[N],len[N],fa[N];
int mx[N],mxx[N],mn[N],mnn[N],dp[N];
int ans[N],anss[N],n,a[N];
char s[N];
struct edge{
	int to,nxt;
}e[N];
void add(int u,int v){
	cnt++;
	e[cnt].nxt=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}
int read(){
	int sum=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'||ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return sum*f;
}
void ins(int id,int c){
	int x=++tot;len[x]=len[u]+1;
	mx[x]=mn[x]=a[id];size[x]=1;mxx[x]=-INF;mnn[x]=INF;
	for(;u&&trans[u][c]==0;u=fa[u])trans[u][c]=x;
	if(u==0)fa[x]=1;
	else{
		int v=trans[u][c];
		if(len[u]+1==len[v])fa[x]=v;
		else{
			int w=++tot;len[w]=len[u]+1;
			mx[w]=-INF;mn[w]=INF;mxx[x]=-INF;mnn[x]=INF;
			memcpy(trans[w],trans[v],sizeof(trans[w]));
			fa[w]=fa[v];
			fa[x]=fa[v]=w;
			for(;u&&trans[u][c]==v;u=fa[u])trans[u][c]=w;
		}
	}
	u=x;
}
void dfs(int u){
	int tmp=size[u];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		dfs(v);
		dp[u]+=size[v]*tmp;
		tmp+=size[v];
		if(mx[v]>mx[u]){
			mxx[u]=mx[u];
			mx[u]=mx[v];
			if(mxx[v]>mxx[u])mxx[u]=mxx[v];
		}
		else if(mx[v]>mxx[u])mxx[u]=mx[v];
		if(mn[v]<mn[u]){
			mnn[u]=mn[u];
			mn[u]=mn[v];
			if(mnn[v]<mnn[u])mnn[u]=mnn[v];
		}
		else if(mn[v]<mnn[u])mnn[u]=mn[v];
		size[u]+=size[v];
	}
	ans[len[u]]+=dp[u];
	if(size[u]!=1)anss[len[u]]=max(anss[len[u]],max(mx[u]*mxx[u],mn[u]*mnn[u]));
}
signed main(){
	scanf("%lld",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int i=n;i>=1;i--)ins(i,s[i]-'a'+1);
	for(int i=1;i<=tot;i++)add(fa[i],i);
	for(int i=0;i<=n+1;i++)anss[i]=-INF;
	dfs(1);
	for(int i=n;i>=0;i--)ans[i]+=ans[i+1],anss[i]=max(anss[i],anss[i+1]);
	for(int i=0;i<n;i++)
		if(ans[i]==0)printf("0 0\n");
		else printf("%lld %lld\n",ans[i],anss[i]);
	return 0;
}
posted @ 2019-01-03 08:25  Xu-daxia  阅读(281)  评论(0编辑  收藏  举报