[NOI2015]品酒大会 [SAM]

传送门

其实没有想象中难, 我们知道, 两个串的最长公共后缀就是 parent 树上的LCA 的len

对于出现次数, 枚举每一个 LCA, 然后 siz 乘一下

因为如果 len 出现了, len - 1 也出现了, 所以求一个后缀和

对于最大值, 我们维护子树最大与次大, 因为有负数, 所以还要维护次小与最小

同样对最大值也求一个后缀最大


#include<bits/stdc++.h>
#define N 600050
using namespace std;
typedef long long ll;
const ll inf = 900000000000000000;
int n; char s[N]; ll a[N];
struct Node{ 
	int ch[26], link, len, siz;
} t[N];
ll mx1[N], mx2[N], mi1[N], mi2[N];
ll sum[N], ans[N];
int last, tot;
void Extend(int Id, int c){
	int cur = ++tot, p = last;
	mx1[cur] = a[Id]; mx2[cur] = -inf; mi1[cur] = a[Id]; mi2[cur] = inf;
	t[cur].len = t[p].len + 1; t[cur].siz = 1;
	for(;p && !t[p].ch[c]; p = t[p].link) t[p].ch[c] = cur;
	if(!p) t[cur].link = 1;
	else{
		int q = t[p].ch[c];
		if(t[q].len == t[p].len + 1) t[cur].link = q;
		else{
			int clone = ++tot; 
			mx1[clone] = mx2[clone] = -inf; mi1[clone] = mi2[clone] = inf;
			t[clone].link = t[q].link;
			t[clone].len = t[p].len + 1;
			for(int i=0; i<26; i++) t[clone].ch[i] = t[q].ch[i];
			for(;p && t[p].ch[c] == q; p = t[p].link) t[p].ch[c] = clone;
			t[cur].link = t[q].link = clone;
		}
	} last = cur;
}

vector<int> v[N];
void mxUpt(ll w, int u){ if(w > mx1[u]) mx2[u] = mx1[u], mx1[u] = w; else if(w > mx2[u]) mx2[u] = w;}
void miUpt(ll w, int u){ if(w < mi1[u]) mi2[u] = mi1[u], mi1[u] = w; else if(w < mi2[u]) mi2[u] = w;}
void dfs(int u){
	for(int i=0; i<v[u].size(); i++){
		int son = v[u][i]; dfs(son);
		sum[t[u].len] += 1ll * t[u].siz * t[son].siz;
		t[u].siz += t[son].siz;
		mxUpt(mx1[son], u); mxUpt(mx2[son], u);
		miUpt(mi1[son], u); miUpt(mi2[son], u);
	} if(t[u].siz < 2) return;
	if(mx2[u] != -inf && mi2[u] != inf) 
		ans[t[u].len] = max(ans[t[u].len], max(mx1[u] * mx2[u], mi1[u] * mi2[u]));
}
int main(){
	scanf("%d%s", &n, s+1); last = tot = 1; mx1[1] = mx2[1] = -inf; mi1[1] = mi2[1] = inf;
	for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
	for(int i=n; i>=1; i--) Extend(i, s[i] - 'a');
	for(int i=2; i<=tot; i++) v[t[i].link].push_back(i);
	for(int i=1; i<=tot; i++) ans[i] = -inf; 
	dfs(1);
	for(int i=n-1; i>=0; i--) ans[i] = max(ans[i], ans[i+1]), sum[i] += sum[i+1];
	for(int i=0; i<n; i++){
		printf("%lld %lld\n", sum[i], sum[i] ? ans[i] : 0);
	} return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值