【BZOJ 4199】[Noi2015]品酒大会 后缀自动机构造后缀树+dp

题面:http://uoj.ac/problem/131(BZOJ肿么了qaq)

如果连后缀自动机为什么可以转成后缀树都不知道的,先去切3238差异:http://blog.csdn.net/pbihao/article/details/53905995

我们先构造出后缀自动机,然后转成后缀树,在后缀树上跑dp,只用维护一个最大最小,和size就好了

#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 600020
#define LL long long
using namespace std;
int n,son[maxn][26],fail[maxn],size[maxn],len[maxn],tot=1,rt=1,last=1,cnt=1,head[maxn],val[maxn];
LL ans1[maxn],ans2[maxn],max1[maxn],min1[maxn];
bool is[maxn];
int dep[maxn],w[maxn];
char s[maxn];
struct edge{int v,next;}e[maxn*2];
void adde(int a,int b){e[cnt].v=b,e[cnt].next=head[a];head[a]=cnt++;}

void insert(int x,int v){
	int p=last,np=++tot,q,nq;
	size[np]=1,len[np]=len[p]+1,is[np]=true;
	while(p&&!son[p][x])son[p][x]=np,p=fail[p];
	if(p==0)fail[np]=rt;
	else{
		int q=son[p][x];
		if(len[q]==len[p]+1)fail[np]=q;
		else{
			nq=++tot;len[nq]=len[p]+1;
			memcpy(son[nq],son[q],sizeof(son[q]));
			fail[nq]=fail[q];
			fail[q]=fail[np]=nq;
			while(p&&son[p][x]==q)son[p][x]=nq,p=fail[p];
		}
	}
	last=np;w[np]=v;
}

void dfs(int u,int fa){
	dep[u]=len[u];
	if(is[u])max1[u]=min1[u]=w[u];
	else max1[u]=-1e15,min1[u]=1e15;
	for(int v,i=head[u];i;i=e[i].next){
		dfs(v=e[i].v,u);
		
		if(max1[u]!=-1e15&&max1[v]!=-1e15&&min1[v]!=1e15&&min1[u]!=1e15)
		ans1[dep[u]]=max(max(ans1[dep[u]],(LL)max1[u]*max1[v]),(LL)min1[u]*min1[v]);
		ans2[dep[u]]+=(LL)size[u]*size[v];
		
		max1[u]=max(max1[u],max1[v]);
		min1[u]=min(min1[u],min1[v]);
		
		size[u]+=size[v];
	}
}

int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)scanf("%d",val+i);
	for(int i=n;i>=1;i--)insert(s[i]-'a',val[i]);
	for(int i=2;i<=tot;i++)
		adde(fail[i],i);
	for(int i=0;i<=n;i++)ans1[i]=-1e18;
	dep[0]=-1;
	dfs(1,0);
	for(int i=n-1;i>=0;i--)
		ans1[i]=max(ans1[i],ans1[i+1]),ans2[i]+=ans2[i+1];
	for(int i=0;i<n;i++)
	if(ans2[i])printf("%lld %lld\n",ans2[i],ans1[i]);
	else puts("0 0");
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值