luogu解题报告:P2178[NOI2015]品酒大会
题意
给定一个字符串S和一个权值函数
My Idea
首先看到标签知道是后缀数组,那么就先搞出height数组。
考虑一个naive的方法:从大到小枚举r,每次只考虑两位置最大为r匹配(而不考虑5匹配也能是3匹配的)。我们建一棵height的线段树。对于每一次,我们先找出height中的每一个height[p] = r的位置,然后向前后二分并用线段树判断,找到最大的使height[p]为最小值的区间,然后左右乘一乘就算出来了。
然而复杂度是当然松爷肯定能卡过去)。
正解
居然是并查集……
最后一步变成边计算边合并,维护一个带权并查集就好了。神思路……
如果用DA就是
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 300005;
struct S_A {
int A[maxn], SA[maxn], C[maxn], rank[maxn], h[maxn], n;
struct p {
int x[2], id;
p(){}
p(int a, int b, int c):id(c) {x[0] = a, x[1] = b;}
} RE[maxn], RT[maxn];
S_A():n(0){}
void radix_sort() {
for (int y = 1; y >= 0; y--) {
memset(C, 0, sizeof C);
register int i;
for (i = 1; i <= n; i++) C[RE[i].x[y]]++;
for (i = 1; i < maxn; i++) C[i] += C[i-1];
for (i = n; i >= 1; i--) RT[C[RE[i].x[y]]--] = RE[i];
for (i = 1; i <= n; i++) RE[i] = RT[i];
}
for (int i = 1; i <= n; i++) {
rank[RE[i].id] = rank[RE[i-1].id];
if (RE[i].x[1] != RE[i-1].x[1] || RE[i].x[0] != RE[i-1].x[0]) rank[RE[i].id]++;
}
}
void build_sa() {
for (int i = 1; i <= n; i++) RE[i] = p(A[i], 0, i);
radix_sort();
for (int k = 1; k < n; k <<= 1) {
for (int i = 1; i <= n; i++) RE[i] = p(rank[i], i+k<=n?rank[i+k]:0, i);
radix_sort();
}
for (int i = 1; i <= n; i++) SA[rank[i]] = i;
}
void build_height() {
for (int ht = 0, i = 1; i <= n; i++) {
if (rank[i] == 1) ht = 0;
else {
int k = SA[rank[i]-1];
if (--ht < 0) ht = 0;
while (A[i+ht] == A[k+ht]) ht++;
}
h[rank[i]] = ht;
}
}
void init(const char *str) {
for (; *str != '\0'; ++str) A[++n] = *str-'a'+1;
build_sa();
build_height();
}
}SA;
char str[maxn];
long long a[maxn];
long long ansx[maxn], ansy[maxn];
struct p {
int to, next;
} edge[maxn];
int head[maxn], top = 0;
void push(int i, int j) {
edge[++top].to = j;
edge[top].next = head[i];
head[i] = top;
}
int fa[maxn];
long long siz[maxn], mx[maxn], mn[maxn];
long long cnt = 0, maxd = LONG_LONG_MIN;
int findp(int i)
{ return fa[i]?fa[i]=findp(fa[i]):i; }
void link(int i, int j) {
//cout << i << " " << j << endl;
if (!i || !j) return;
int p = findp(i), q = findp(j);
if (p != q) {
maxd = max(maxd, max(mn[p]*mn[q], max(mx[p]*mx[q], max(mn[p]*mx[q], mx[p]*mn[q]))));
cnt += siz[p]*siz[q];
fa[p] = q;
siz[q] += siz[p];
mx[q] = max(mx[q], mx[p]);
mn[q] = min(mn[q], mn[p]);
}
}
int main() {
int n; scanf("%d", &n);
scanf("%s", str);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), siz[i] = 1, fa[i] = 0;
SA.init(str);
for (int i = 1; i <= n; i++) mn[i] = mx[i] = a[SA.SA[i]];
for (int i = 1; i <= n; i++) push(SA.h[i], i);
for (int i = n-1; i >= 0; i--) {
for (int j = head[i]; j; j = edge[j].next) {
int to = edge[j].to, p = findp(to), q = findp(to-1);
maxd = max(maxd, max(mn[p]*mn[q], max(mx[p]*mx[q], max(mn[p]*mx[q], mx[p]*mn[q]))));
}
for (int j = head[i]; j; j = edge[j].next) {
int to = edge[j].to;
link(to-1, to);
}
ansx[i] = cnt;
ansy[i] = maxd;
if (ansy[i] == LONG_LONG_MIN) ansy[i] = 0;
}
for (int i = 0; i < n; i++)
printf("%lld %lld\n", ansx[i], ansy[i]);
return 0;
}