码迷,mamicode.com
首页 > 其他好文 > 详细

【uoj131】 NOI2015—品酒大会

时间:2017-01-22 08:09:37      阅读:199      评论:0      收藏:0      [点我收藏+]

标签:get   ring   end   ret   cst   print   log   后缀树   pac   

http://uoj.ac/problem/131 (题目链接)

题意

  给出一个字符串,每个后缀有一个权值${a_i}$,这些后缀两两之间存在公共前缀。问能够组成长度从0~n-1的公共前缀的后缀的方案数以及他们权值的最大乘积。

Solution

  听LCF说这是水题,就来做了。。

  lyp学长说SAM构出来后就两个东西:在自动机上dp,在后缀树上搞事情。

  于是我们很容易想到在后缀树上dp(感觉在自动机上dp没什么卵用),于是我们把串反过来见后缀自动机,然后建出后缀树,在上面树形dp就可以了。

  假设当前dfs到了节点x。我们用mx[x][0,1]记录当前子树中最大的两个后缀的权值,因为权值可能为负数,所以还要用mn[x][0,1]记录当前子树中最小的两个后缀的权值,sum[x]记录当前子树中后缀的个数。这样你发现这些信息就能够更新长度为0~len[x]的答案,其中len[x]表示其在后缀树中的“深度”(其实深度这个表示并不是特别妥当→_→),那么就可以做了。

细节

  细节多的抠脚,建议想清楚再写→_→

代码

// uoj131
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1ll<<60
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
 
const int maxn=300010;
int n;
char s[maxn];
LL a[maxn],ans[maxn],cnts[maxn];

namespace SAM {
	int len[maxn<<1],par[maxn<<1],pos[maxn<<1],ch[maxn<<1][26];
	int last,Dargen,sz,cnt,head[maxn<<1];
	LL mx[maxn<<1][2],mn[maxn<<1][2],sum[maxn<<1];

	struct edge {int to,next;}e[maxn<<2];
	
	void link(int u,int v) {
		e[++cnt]=(edge){v,head[u]};head[u]=cnt;
	}
	void Extend(int c) {
		int np=++sz,p=last;last=np;
		len[np]=pos[np]=len[p]+1;
		for (;p && !ch[p][c];p=par[p]) ch[p][c]=np;
		if (!p) par[np]=Dargen;
		else {
			int q=ch[p][c];
			if (len[p]+1==len[q]) par[np]=q;
			else {
				int nq=++sz;len[nq]=len[p]+1;
				memcpy(ch[nq],ch[q],sizeof(ch[q]));
				par[nq]=par[q];
				par[np]=par[q]=nq;
				for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;
			}
		}
	}
	void build() {
		last=Dargen=sz=1;
		for (int i=1;i<=n>>1;i++) swap(s[i],s[n-i+1]),swap(a[i],a[n-i+1]);
		for (int i=1;i<=n;i++) Extend(s[i]-‘a‘);
	}
	void dfs(int x) {
		mx[x][0]=mx[x][1]=-inf;
		mn[x][0]=mn[x][1]=inf;
		if (pos[x]) mn[x][0]=mx[x][0]=a[pos[x]],sum[x]=1;
		for (int i=head[x];i;i=e[i].next) {
			dfs(e[i].to);
			cnts[len[x]]+=sum[x]*sum[e[i].to];
			sum[x]+=sum[e[i].to];
			if (mx[x][0]<mx[e[i].to][0]) {
				mx[x][1]=mx[x][0],mx[x][0]=mx[e[i].to][0];
			}
			else if (mx[x][1]<mx[e[i].to][0]) mx[x][1]=mx[e[i].to][0];
			if (mx[x][1]<mx[e[i].to][1]) mx[x][1]=mx[e[i].to][1];
			if (mn[x][0]>mn[e[i].to][0]) {
				mn[x][1]=mn[x][0],mn[x][0]=mn[e[i].to][0];
			}
			else if (mn[x][1]>mn[e[i].to][0]) mn[x][1]=mn[e[i].to][0];
			if (mn[x][1]>mn[e[i].to][1]) mn[x][1]=mn[e[i].to][1];
		}
		if (mx[x][0]!=-inf && mx[x][1]!=-inf) ans[len[x]]=max(ans[len[x]],mx[x][0]*mx[x][1]);
		if (mn[x][0]!=inf && mn[x][1]!=inf) ans[len[x]]=max(ans[len[x]],mn[x][0]*mn[x][1]);
	}
	void prepare() {
		for (int i=2;i<=sz;i++) link(par[i],i);
		for (int i=0;i<=n;i++) ans[i]=-inf;
		dfs(Dargen);
	}
}
using namespace SAM;

int main() {
	scanf("%d",&n);
	scanf("%s",s+1);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build();
	prepare();
	for (int i=n-1;i>=0;i--) {
		ans[i]=max(ans[i],ans[i+1]);
		cnts[i]+=cnts[i+1];
	}
	for (int i=0;i<n;i++) printf("%lld %lld\n",cnts[i],ans[i]==-inf ? 0 : ans[i]);
	return 0;
}

  

【uoj131】 NOI2015—品酒大会

标签:get   ring   end   ret   cst   print   log   后缀树   pac   

原文地址:http://www.cnblogs.com/MashiroSky/p/6339311.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!