bzoj4199 [Noi2015]品酒大会
Description
一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。
在大会的晚餐上,调酒师 Rainbow 调制了 \(n\) 杯鸡尾酒。这 \(n\) 杯鸡尾酒排成一行,其中第 \(i\) 杯酒 \((1 \le i \le n)\) 被贴上了一个标签 \(s_i\),每个标签都是 \(26\) 个小写英文字母之一。设 \(str(i,j)\) 表示第 \(i\) 杯酒到第 \(j\) 杯酒的 \(j−i+1\) 个标签顺次连接构成的字符串。若 \(str(l_1, r_1)=str(l_2, r_2)\),其中\(1 \le l_1 \le r_1 \le n, 1 \le l_2 \le r_2 \le n, l_1 \ne l_2, r_1 − l_1 + 1 = r_2 − l_2 + 1 = p\) ,则称第 \(l_1\) 杯酒与第 \(l_2\) 杯酒是“ \(p\) 相似”的。当然两杯“ \(p\) 相似” \(( p>1)\) 的酒同时也是“ \(1\) 相似”、“\(2\) 相似”、……、“\((p−1)\) 相似”的。特别地,对于任意的 \(1 \le p, q \le n, p \ne q\),第 \(p\) 杯酒和第 \(q\) 杯酒都是“\(0\) 相似”的。
在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 \(i\) 杯酒 \((1 \le i \le n)\) 的美味度为 \(a_i\) 。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 \(i\) 杯酒与第 \(j\) 杯酒调兑在一起,将得到一杯美味度为 \(a_i ∗ a_j\) 的酒。现在请各位品酒师分别对于 \(p = 0, 1, 2,..., p−1\) ,统计出有多少种方法可以选出 \(2\) 杯“ \(p\) 相似”的酒,并回答选择 \(2\) 杯“ \(p\) 相似”的酒调兑可以得到的美味度的最大值。
Input
输入文件的第 \(1\) 行包含 \(1\) 个正整数 \(n\) ,表示鸡尾酒的杯数。
第 \(2\) 行包含一个长度为 \(n\) 的字符串 \(S\) ,其中第 \(i\) 个字符表示第 \(i\) 杯酒的标签。
第 \(3\) 行包含 \(n\) 个整数,相邻整数之间用单个空格隔开,其中第 \(i\) 个整数表示第 \(i\) 杯酒的美味度 \(a_i\) 。
\(n\le300000, |a_i |\le1000000000\)
Output
输出文件包括 \(n\) 行。第 \(i\) 行输出 \(2\) 个整数,中间用单个空格隔开。第 \(1\) 个整数表示选出两杯“ \((i-1)\) 相似”的酒的方案数,第 \(2\) 个整数表示选出两杯“$ (i-1)$ 相似”的酒调兑可以得到的最大美味度。若不存在两杯“ \((i-1)\) 相似”的酒,这两个数均为 \(0\) 。
Sample Input
10
ponoiiipoi
2 1 4 7 4 8 3 6 4 7
Sample Output
45 56
10 56
3 32
0 0
0 0
0 0
0 0
0 0
0 0
0 0
Solution
这暑假题要是没做出来怕不是要被 lxs 关起来
第一问直接建出后缀数组,用 \(height[]\) 算出每一个点有贡献的区间。
第二问就用个并查集搞一搞就好了
#include<bits/stdc++.h>
using namespace std;
#define N 300001
#define drp(i, a, b) for (int i = a; i >= b; i--)
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define ll long long
inline int read() {
int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}
int n;
char s[N];
ll sum[N],ans[N];
int m, t1[N], t2[N], c[N], sa[N], rank[N], height[N];
int fa[N], mx[N], mn[N], a[N], stk[N], l[N], r[N], top = 1;
struct edge{ int v, next; }e[N];
int head[N], tot;
inline bool cmp(int *y, int p, int q, int k){
return (p + k >= n ? -1 : y[p + k]) == (q + k >= n ? -1 : y[q + k]) && y[p] == y[q];
}
inline void build_sa() {
int *x = t1, *y = t2; m = 26;
rep(i, 0, m - 1) c[i] = 0;
rep(i, 0, n - 1) ++c[x[i] = s[i] - 'a'];
rep(i, 1, m - 1) c[i] += c[i - 1];
drp(i, n - 1, 0) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1) {
int p = 0;
rep(i, n - k, n - 1) y[p++] = i;
rep(i, 0, n - 1) if(sa[i] >= k) y[p++] = sa[i] - k;
rep(i, 0, m - 1) c[i] = 0;
rep(i, 0, n - 1) ++c[x[y[i]]];
rep(i, 1, m - 1) c[i] += c[i - 1];
drp(i, n - 1, 0) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
x[sa[0]]=0; m = 1;
rep(i, 1, n - 1) x[sa[i]] = cmp(y, sa[i], sa[i - 1], k) ? m - 1 : m++;
if(m >= n) break;
}
}
inline void build_height() {
int k = 0;
rep(i, 0, n - 1) rank[sa[i]] = i;
rep(i, 0, n - 1) if(rank[i]) {
if(k) k--;
int j = sa[rank[i]-1];
while(!(s[i + k] ^ s[j + k])) k++;
height[rank[i]] = k;
}
}
inline void add(int u, int v) { e[++tot].v = v, e[tot].next = head[u], head[u] = tot; }
inline int find(int x) { return fa[x] != x ? find(fa[x]) : fa[x]; }
#define Max(a, b) a = max(a, b)
#define Min(a, b) a = min(a, b)
int main() {
n = read();
rep(i, 0, n - 1) { char ch = getchar(); while(ch < 'a' || ch > 'z') ch = getchar(); s[i] = ch; }
build_sa(), build_height();
rep(i, 0, n - 1) fa[i] = i, a[i] = mx[rank[i]] = mn[rank[i]] = read();
l[0] = 0;
rep(i, 1, n - 1) {
while(top && height[stk[top]] > height[i]) --top;
if(top) l[i] = stk[top] + 1;
stk[++top]=i;
}
r[stk[top = 1] = n - 1] = n - 1;
drp(i, n - 2, 0) {
while(top && height[stk[top]] >= height[i]) --top;
r[i] = top ? stk[top] - 1 : n - 1;
stk[++top]=i;
}
rep(i, 0, n - 1) add(height[i], i);
memset(ans, 128, sizeof ans);
drp(i, n - 1, 1) {
sum[i] = sum[i + 1], ans[i] = ans[i + 1];
for(int j = head[i]; j; j = e[j].next) {
int t = e[j].v, L = l[t], R = r[t], p = find(L - 1), q = find(R);
sum[i] += (ll)(t - L + 1) * (R - t + 1);
Max(ans[i], max((ll)mx[p] * mx[q], (ll)mn[p] * mn[q]));
Max(mx[p], mx[q]), Min(mn[p], mn[q]), fa[q] = p;
}
}
sort(a, a + n);
ans[0] = max((ll)a[0] * a[1], (ll)a[n - 1] * a[n - 2]);
sum[0] = (ll)n * (n - 1) / 2;
rep(i, 0, n - 1) printf("%lld %lld\n", sum[i], sum[i] ? ans[i] : 0);
return 0;
}