# Description
Luogu 传送门
# Solution
首先有一个明显的结论,如果两个子串是 相似,那么它们也一定是 相似。
所以我们可以通过 SA 把 height 数组求出来,然后从大到小排序,并依次枚举。
每次对两个相邻的后缀进行处理,把它们两个所在的并查集合并。
考虑合并都有哪些操作,回顾一下问题,我们需要计算 相似的个数及最大权值。
先看个数:由于我们是按 height 数组从大到小枚举的,所以已经在并查集里的子串一定是合法的,所以
最大权值:这里我们需要开个数组记录一下每个并查集内的最大值和最小值(可能出现负负得正的情况),然后
至此这道题就做完啦!
# Code
#include <bits/stdc++.h> | |
#define ll long long | |
using namespace std; | |
const ll N = 3e5 + 10; | |
ll n, m = 130; | |
char s[N]; | |
ll a[N], rk[N], cnt[N], sa[N], tmp[N], hei[N]; | |
inline void update(ll Rk[], ll tp[]){ | |
for(ll i = 1; i <= m; ++i) cnt[i] = 0; | |
for(ll i = 1; i <= n; ++i) cnt[Rk[tp[i]]]++; | |
for(ll i = 1; i <= m; ++i) cnt[i] += cnt[i - 1]; | |
for(ll i = n; i; --i) sa[cnt[Rk[tp[i]]]--] = tp[i]; | |
} | |
inline void build_sa(){ | |
ll *Rk = rk, *tp = tmp; | |
for(ll i = 1; i <= n; ++i) Rk[i] = s[i], tp[i] = i; | |
update(Rk, tp); | |
ll p = 1; | |
for(ll len = 1; p < n; m = p, len <<= 1){ | |
p = 0; | |
for(ll i = n - len + 1; i <= n; ++i) tp[++p] = i; | |
for(ll i = 1; i <= n; ++i) | |
if(sa[i] > len) tp[++p] = sa[i] - len; | |
update(Rk, tp); | |
swap(Rk, tp); | |
Rk[sa[1]] = p = 1; | |
for(ll i = 2; i <= n; ++i) | |
Rk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]]) && (tp[sa[i] + len] == tp[sa[i - 1] + len]) ? p : ++p; | |
} | |
for(ll i = 1; i <= n; ++i) rk[sa[i]] = i; | |
for(ll i = 1, j = 0; i <= n; hei[rk[i++]] = j) | |
for(j = j ? j - 1 : j; s[i + j] == s[sa[rk[i] - 1] + j]; ++j); | |
} | |
ll f[N], id[N], ans[N][2]; | |
ll siz[N], maxs[N], mins[N]; | |
inline bool cmp(ll x, ll y){ | |
return hei[x] > hei[y]; | |
} | |
inline ll find(ll x){ | |
return f[x] == x ? x : f[x] = find(f[x]); | |
} | |
inline void Union(ll x, ll y){ | |
ll k = hei[x]; | |
x = find(x), y = find(y); | |
ans[k][0] += siz[x] * siz[y]; | |
ans[k][1] = max(ans[k][1], max(maxs[x] * maxs[y], mins[x] * mins[y])); | |
mins[x] = min(mins[x], mins[y]); | |
maxs[x] = max(maxs[x], maxs[y]); | |
f[y] = x, siz[x] += siz[y]; | |
} | |
inline void solve(){ | |
for(ll i = 1; i <= n; ++i) | |
f[i] = i, siz[i] = 1, mins[i] = maxs[i] = a[sa[i]]; | |
for(ll i = 1; i <= n; ++i) Union(id[i], id[i] - 1); | |
} | |
signed main(){ | |
scanf("%lld%s", &n, s + 1); | |
for(ll i = 1; i <= n; ++i) scanf("%lld", &a[i]); | |
build_sa(); | |
for(ll i = 1; i <= n; ++i) | |
id[i] = i, ans[i][0] = 0, ans[i][1] = -1e18; | |
sort(id + 1, id + 1 + n, cmp); | |
solve(); | |
for(ll i = n - 1; i >= 0; --i){ | |
ans[i][0] += ans[i + 1][0]; | |
ans[i][1] = max(ans[i][1], ans[i + 1][1]); | |
} | |
for(ll i = 0; i < n; ++i) | |
printf("%lld %lld\n", ans[i][0], !ans[i][0] ? 0 : ans[i][1]); | |
return 0; | |
} |