「NOI2015」品酒大會
阿新 • • 發佈:2020-08-18
知識點: SA,並查集
原題面 Loj Luogu
「そして誰もいなくなるか?」
題意簡述
給定一字串 \(S\),位置 \(i\) 的屬性值為 \(a_i\)。
定義位置 \(p,q\) 為「 \(r\) 相似」,當且僅當 \(S[p:p+r-1] = S[q:q+r-1]\) 成立。
特別地,對於任意 \(1\le p,q\le n, p\not ={q}\),\(p,q\) 都是「 \(0\) 相似」的。
求:選出兩個 「 \(0\sim n-1\) 相似」 的位置的 方案數,及選出兩個 「 \(0\sim n-1\) 相似」的位置屬性值 乘積的最大值。
分析題意
程式碼實現
//知識點:SA,並查集 /* By:Luckyblock */ #include <algorithm> #include <cstdio> #include <cstring> #include <ctype.h> #include <set> #define ll long long const int kMaxn = 3e5 + 10; //============================================================= char S[kMaxn]; int n, m, a[kMaxn]; int cnt[kMaxn], id[kMaxn], rkid[kMaxn]; int sa[kMaxn], rk[kMaxn << 1], oldrk[kMaxn << 1], height[kMaxn]; ll ans1[kMaxn], ans2[kMaxn]; int fa[kMaxn], size[kMaxn]; std :: multiset <int> s[kMaxn]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void GetMax(ll &fir, ll sec) { if (sec > fir) fir = sec; } bool cmp(int x, int y, int w) { //判斷兩個子串是否相等。 return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w]; } void GetHeight() { for (int i = 1, k = 0; i <= n; ++ i) { if (rk[i] == 1) k = 0; else { if (k > 0) k --; int j = sa[rk[i] - 1]; while (i + k <= n && j + k <= n && S[i + k] == S[j + k]) { ++ k; } } height[rk[i]] = k; } } void SuffixSort() { m = 300; for (int i = 1; i <= n; ++ i) ++ cnt[rk[i] = S[i]]; for (int i = 1; i <= m; ++ i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; -- i) sa[cnt[rk[i]] --] = i; for (int p, w = 1; w < n; w <<= 1) { p = 0; for (int i = n; i > n - w; -- i) id[++ p] = i; for (int i = 1; i <= n; ++ i) { if (sa[i] > w) id[++ p] = sa[i] - w; } memset(cnt, 0, sizeof (cnt)); for (int i = 1; i <= n; ++ i) ++ cnt[(rkid[i] = rk[id[i]])]; for (int i = 1; i <= m; ++ i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; -- i) sa[cnt[rkid[i]] --] = id[i]; std ::swap(rk, oldrk); m = 0; for (int i = 1; i <= n; ++ i) { m += (cmp(sa[i], sa[i - 1], w) ^ 1); rk[sa[i]] = m; } } GetHeight(); } bool Compare(int fir, int sec) { return height[fir] > height[sec]; } int Find(int x) { return fa[x] == x ? x : fa[x] = Find(fa[x]); } void Union(int x, int y, int z) { x = Find(x), y = Find(y); if (size[x] < size[y]) std :: swap(x, y); ans1[z] += 1ll * size[x] * size[y]; for (std :: set <int> :: iterator it = s[y].begin(); it != s[y].end(); ++ it) { s[x].insert(* it); } int t[5]; t[1] = *s[x].begin(), t[2] = *(++ s[x].begin()); t[3] = *(-- s[x].end()), t[4] = *(-- (-- s[x].end())); GetMax(ans2[z], std :: max(1ll * t[1] * t[2], 1ll * t[3] * t[4])); fa[y] = x; size[x] += size[y]; if (s[x].size() > 5) { s[x].clear(); for (int i = 1; i <= 4; ++ i) s[x].insert(t[i]); } } //============================================================= int main() { n = read(); scanf("%s", S + 1); for (int i = 1; i <= n; ++ i) a[i] = read(); SuffixSort(); memset(ans2, - 63, sizeof (ans2)); for (int i = 2; i <= n; ++ i) id[i] = i; for (int i = 1; i <= n; ++ i) fa[i] = i; for (int i = 1; i <= n; ++ i) size[i] = 1; std :: sort(id + 2, id + n + 1, Compare); for (int i = 1; i <= n; ++ i) s[i].insert(a[i]); for (int i = 2; i <= n; ++ i) { Union(sa[id[i] - 1], sa[id[i]], height[id[i]]); } for (int i = n - 1; i >= 0; -- i) { ans1[i] += ans1[i + 1]; GetMax(ans2[i], ans2[i + 1]); } for (int i = 0; i < n; ++ i) { printf("%lld %lld\n", ans1[i], ans1[i] ? ans2[i] : 0); } return 0; }