一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。
在大会的晚餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 n 杯酒 (1 ≤ i ≤ n) 被贴上了一个标签si,每个标签都是 26 个小写 英文字母之一。设 str(l, r)表示第 l 杯酒到第 r 杯酒的 r − l + 1 个标签顺次连接构成的字符串。若 str(p, po) = str(q, qo),其中 1 ≤ p ≤ po ≤ n, 1 ≤ q ≤ qo ≤ n, p ≠ q, po − p + 1 = qo − q + 1 = r ,则称第 p 杯酒与第 q 杯酒是“ r 相似” 的。当然两杯“ r 相似”(r > 1)的酒同时也是“ 1 相似”、“ 2 相似”、……、“ (r − 1) 相似”的。特别地,对于任意的 1 ≤ p , q ≤ n , p ≠ q ,第 p 杯酒和第 q 杯酒都 是“ 0 相似”的。
在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 i 杯酒 (1 ≤ i ≤ n) 的 美味度为 ai 。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 p 杯酒与第 q 杯酒调兑在一起,将得到一杯美味度为 ap*aq 的 酒。现在请各位品酒师分别对于 r = 0,1,2, ⋯ , n − 1 ,统计出有多少种方法可以 选出 2 杯“ r 相似”的酒,并回答选择 2 杯“ r 相似”的酒调兑可以得到的美味度的最大值。
思路:
很容易想到后缀数组,然后就不会了。。。
考虑将height数组从大到小排序,然后按顺序每次将其两边的后缀集合合并在一个集合内(用并查集即可,最开始所有后缀都在不同集合内)。
由于是按从大到小的顺序排序的,若从这两个集合内的分别任意选取两个后缀,其LCP必等于h[i], 这两个集合的合并对任意r<= h[i]的答案都有其大小乘积的贡献。
最大值同样维护即可,注意由于可能有负数,所以可以再维护一个最小值。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define For(i,j,k) for(int i = j;i <= k;i++)
#define Forr(i,j,k) for(int i = j;i >= k;i--)
const int N = 300010;
using namespace std;
char S[N];
int sa[N], rank[N], h[N], T1[N], T2[N], c[N], n;
void buildsa(int m){
int *x = T1, *y = T2;
For(i,1,n) c[x[i] = S[i]]++;
For(i,1,m) c[i] += c[i-1];
Forr(i,n,1) sa[c[x[i]]--] = i;
for(int k = 1;k < n;k <<= 1){
int p = 0;
For(i,n-k+1,n) y[++p] = i;
For(i,1,n) if(sa[i] > k) y[++p] = sa[i] - k;
For(i,1,m) c[i] = 0;
For(i,1,n) ++c[x[y[i]]];
For(i,1,m) c[i] += c[i-1];
Forr(i,n,1) sa[c[x[y[i]]]--] = y[i];
swap(x, y);
x[sa[1]] = p = 1;
For(i,2,n) x[sa[i]] = y[sa[i]] == y[sa[i-1]] &&
y[sa[i] + k] == y[sa[i-1] + k] ? p : ++p;
if(p >= n) break;
m = p;
}
}
bool cmp(int x, int y){
return h[x] > h[y];
}
void buildheight(){
For(i,1,n) rank[sa[i]] = i;
int p = 0;
For(i,1,n){
if(p) --p;
int j = sa[rank[i] + 1];
if(!j) continue;
while(S[i + p] == S[j + p]) ++p;
h[rank[i]] = p;
}
For(i,1,n) c[i] = i;
sort(c + 1, c + n, cmp);
}
int fa[N], sz[N], Max[N], Min[N];
long long cnt[N], Maxa[N];
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int main(){
scanf("%d%s", &n, S + 1);
buildsa('z'), buildheight();
For(i,1,n){
int t = rank[i];
scanf("%d", &Max[t]);
fa[t] = t, sz[t] = 1, Min[t] = Max[t], Maxa[i-1] = -5e18;
}
For(i,1,n-1){
int x = find(c[i]), y = find(c[i] + 1);
cnt[h[c[i]]] += 1LL * sz[x] * sz[y];
Maxa[h[c[i]]] = max(Maxa[h[c[i]]],
max(1LL * Min[x] * Min[y], 1LL * Max[x] * Max[y]));
sz[y] += sz[x];
Max[y] = max(Max[y], Max[x]), Min[y] = min(Min[y], Min[x]);
fa[x] = y;
}
Forr(i,n-2,0)
cnt[i] += cnt[i+1], Maxa[i] = max(Maxa[i], Maxa[i+1]);
For(i,0,n-1) printf("%lld %lld\n", cnt[i], cnt[i] ? Maxa[i] : 0);
return 0;
}