bzoj4199 [NOI2015] 品酒大會

傳送門
利用了後綴自動機的一個性質:一個串T的後綴自動機的parent樹就是它的反串的後綴樹
而權值又恰好記在一個串的開頭。於是可以把串反過來建立後綴自動機,然後在這個parent樹(也就是原串的後綴樹)上DP。
兩個節點的lca就是它們的最長公共前綴。
注意,權值範圍在1e9,那麼初始值起碼要1e18,因爲是兩個乘起來。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=4e5+10;
const ll MX=9e18;
const ll MN=-9e18;
int last,sz,n;char s[maxn];
int Head[maxn<<1],Next[maxn<<1],V[maxn<<1],cnt=0;
ll ans1[maxn],ans2[maxn],a[maxn];
struct node{int link,len,right,nxt[26];ll mx,mn;}st[maxn<<1];
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
inline void init(){
	fill(ans2,ans2+n,MN);
	last=sz=0,st[0].link=-1,st[0].len=0;
	st[0].mn=MX,st[0].mx=MN,st[0].right=0;
}
inline void build(int c,ll val){
	int cur=++sz,p=last;
	st[cur].len=st[last].len+1,st[cur].right=1;
	st[cur].mx=st[cur].mn=val;
	for(;p!=-1&&!st[p].nxt[c];p=st[p].link)
		st[p].nxt[c]=cur;
	if(p==-1) st[p].link=0;
	else{
		int q=st[p].nxt[c];
		if(st[p].len+1==st[q].len) st[cur].link=q;
		else{
			int clone=++sz;
			st[clone]=st[q],st[clone].len=st[p].len+1;
			st[clone].right=0,st[clone].mx=MN,st[clone].mn=MX;
			for(;p!=-1&&st[p].nxt[c]==q;p=st[p].link)
				st[p].nxt[c]=clone;
			st[cur].link=st[q].link=clone;
		}
	}last=cur;
}
inline void dfs(int u){
	int L=st[u].len;
	for(int i=Head[u];i;i=Next[i]){
		dfs(V[i]),ans1[L]+=(ll)st[u].right*st[V[i]].right;
		if(st[u].mx!=MN) ans2[L]=max(ans2[L],st[u].mx*st[V[i]].mx);
		if(st[u].mn!=MX) ans2[L]=max(ans2[L],st[u].mn*st[V[i]].mn);
		st[u].mx=max(st[u].mx,st[V[i]].mx),st[u].mn=min(st[u].mn,st[V[i]].mn);
		st[u].right+=st[V[i]].right;
	}
}
int main(){
	//freopen("2905.in","r",stdin);
	scanf("%d%s",&n,s),init();
	for(int i=0;i<n;++i) scanf("%lld",&a[i]);
	for(int i=n-1;i>=0;--i) build(s[i]-'a',a[i]);
	for(int i=1;i<=sz;++i) add(st[i].link,i);
	dfs(0);
	for(int i=n-2;i>=0;i--) ans1[i]+=ans1[i+1],ans2[i]=max(ans2[i],ans2[i+1]);
	for(int i=0;i<n;++i) printf("%lld %lld\n",ans1[i],ans1[i]?ans2[i]:0);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章