[NOI2015]品酒大會 字尾陣列+並差集
阿新 • • 發佈:2018-12-10
Descripition 給出一個長度為n的字串,每個字元有一個權值。 定義兩個子串滿足r相似,當且僅當兩個串長度為r,且字元都相等,那麼這兩個串的配對權值為開頭權值相乘。 先讓你求滿足0~n-1相似的 子串有多少對。 最大的配對權值。
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
先寫個字尾陣列,求個height。 對於height從大到小排序,設當前height的id為i 則將sa[i]與sa[i+1]合併,維護一個最大值,次大值,最小值,次小值,集合元素數。 我用的是並差集。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL _min(LL x, LL y) {return x < y ? x : y;}
LL _max(LL x, LL y) {return x > y ? x : y;}
typedef unsigned long long ULL;
const int P = 131;
int read() {
int s = 0, f = 1 ; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
struct node {
int x, id;
} height[310000];
char ss[310000];
int n, m, a[310000], c[310000];
int sa[310000], Rank[310000 ], Rsort[310000], tt[310000], yy[310000];
LL mn1[310000], mn2[310000], mx1[310000], mx2[310000], tot[310000];
LL ans1[310000], ans2[310000];
ULL s[310000], o[310000];
int fa[310000];
bool cmp(node a, node b) {return a.x > b.x;}
int findfa(int x) {
if(fa[x] != x) fa[x] = findfa(fa[x]);
return fa[x];
}
void get_sa() {
memcpy(Rank, a, sizeof(Rank));
memset(Rsort, 0, sizeof(Rsort));
for(int i = 1; i <= n; i++) Rsort[Rank[i]]++;
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = n; i >= 1; i--) sa[Rsort[Rank[i]]--] = i;
int ln = 1, p = 0;
while(p < n) {
int k = 0;
for(int i = n - ln + 1; i <= n; i++) yy[++k] = i;
for(int i = 1; i <= n; i++) if(sa[i] - ln > 0) yy[++k] = sa[i] - ln;
memset(Rsort, 0, sizeof(Rsort));
for(int i = 1; i <= n; i++) Rsort[Rank[i]]++;
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = n; i >= 1; i--) sa[Rsort[Rank[yy[i]]]--] = yy[i];
for(int i = 1; i <= n; i++) tt[i] = Rank[i];
p = 1; Rank[sa[1]] = 1;
for(int i = 2; i <= n; i++) {
if(tt[sa[i]] != tt[sa[i - 1]] || tt[sa[i] + ln] != tt[sa[i - 1] + ln]) p++;
Rank[sa[i]] = p;
} m = p, ln *= 2;
}
}
LL sum(int x) {
return (LL) x * (x - 1) / 2;
}
int main() {
n = read();
scanf("%s", ss + 1);
for(int i = 1; i <= n; i++) a[i] = ss[i] - 'a' + 1, s[i] = s[i - 1] * P + ss[i];
o[0] = 1; for(int i = 1; i <= n; i++) o[i] = o[i - 1] * P;
m = 26; get_sa();
for(int i = 1; i <= n; i++) c[i] = read();
for(int i = 1; i < n; i++) {
height[i].id = i;
int l = 0, r = _min(n - sa[i] + 1, n - sa[i + 1] + 1), ans = 0;
while(l <= r) {
int mid = (l + r) / 2;
if(s[sa[i] + mid - 1] - s[sa[i] - 1] * o[mid] == s[sa[i + 1] + mid - 1] - s[sa[i + 1] - 1] * o[mid]) l = mid + 1, ans = mid;
else r = mid - 1;
} height[i].x = ans;
} sort(height + 1, height + n, cmp);
LL ans = 0, ll = 0, last = n - 1; bool bk = 0;
for(int i = 1; i <= n; i++) fa[i] = i, mn1[i] = mx1[i] = c[i], mn2[i] = 999999999, mx2[i] = -999999999, tot[i] = 1;
for(int i = 1; i <= n; i++) {
while(height[i].x < last) ans1[last] = ll, ans2[last] = ans, --last;
int x = sa[height[i].id], y = sa[height[i].id + 1];
int fx = findfa(x), fy = findfa(y);
if(fy != fx) {
fa[fx] = fy;
ll -= sum(tot[fx]) + sum(tot[fy]);
tot[fy] += tot[fx];
ll += sum(tot[fy]);
if(mn1[fx] < mn1[fy]) {
mn2[fy] = mn1[fy];
mn1[fy] = mn1[fx];
} else if(mn1[fx] < mn2[fy]) mn2[fy] = mn1[fx];
if(mn2[fx] < mn2[fy]) mn2[fy] = mn2[fx];
if(mx1[fx] > mx1[fy]) {
mx2[fy] = mx1[fy];
mx1[fy] = mx1[fx];
} else if(mx1[fx] > mx2[fy]) mx2[fy] = mx1[fx];
if(mx2[fx] > mx2[fy]) mx2[fy] = mx2[fx];
if(!bk) ans = (LL)mx1[fy] * mx2[fy], bk = 1;
else ans = _max(ans, (LL)mx1[fy] * mx2[fy]);
ans = _max(ans, (LL)mn1[fy] * mn2[fy]);
}
} printf("%lld %lld\n", ll, ans);
for(int i = 1; i < n; i++) printf("%lld %lld\n", ans1[i], ans2[i]);
return 0;
}