1. 程式人生 > >[NOI2015]品酒大會 字尾陣列+並差集

[NOI2015]品酒大會 字尾陣列+並差集

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; }