AcWing 1004. 品酒大會 (字尾陣列,並查集)
阿新 • • 發佈:2021-09-06
解題思路
從字尾陣列的角度考慮,利用\(lcp\)的性質,\(lcp(i, j) = min(lcp(k_1, k_2), i \neq k_1,k_2 \neq j\)。那麼對於所有“r相似”的字尾來說他們之間的\(lcp\)肯定是大於等於\(r\)的。我們可以考慮根據lcp的大小按順序將所有後綴加入集合,從而維護一系列lcp大於等於r的集合進而求得所有符合條件的字尾對數,這就是經典的並查集維護集合問題。
程式碼
const int maxn = 3e5+10; char s[maxn]; int n, m; int sa[maxn], x[maxn], y[maxn], c[maxn], rk[maxn], h[maxn]; void get_sa() { for (int i = 1; i<=n; ++i) ++c[x[i]=s[i]]; for (int i = 2; i<=m; ++i) c[i] += c[i-1]; for (int i = n; i; --i) sa[c[x[i]]--] = i; for (int k = 1; k<=n; k<<=1) { int num = 0; for (int i = n-k+1; i<=n; ++i) y[++num] = i; for (int i = 1; i<=n; ++i) if (sa[i]>k) y[++num] = sa[i]-k; for (int i = 1; i<=m; ++i) c[i] = 0; for (int i = 1; i<=n; ++i) ++c[x[i]]; for (int i = 1; i<=m; ++i) c[i] += c[i-1]; for (int i = n; i; --i) sa[c[x[y[i]]]--] = y[i], y[i] = 0; swap(x, y); x[sa[1]] = 1, num = 1; for (int i = 2; i<=n; ++i) x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num; if (num==n) break; m = num; } } vector<P> g[maxn]; void get_h() { for (int i = 1; i<=n; ++i) rk[sa[i]] = i; for (int i = 1, k = 0; i<=n; ++i) { if (rk[i]==1) continue; if (k) --k; int j = sa[rk[i]-1]; while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) ++k; h[rk[i]] = k; g[k].push_back({sa[rk[i]], sa[rk[i]-1]}); } } int p[maxn]; ll sz[maxn], mx1[maxn], mx2[maxn], mi1[maxn], mi2[maxn]; int fd(int x) { return p[x]==x ? p[x]:p[x]=fd(p[x]); } ll sum = 0, ans = -2e18; void calc(int x) { for (auto t :g[x]) { int fa = fd(t.x); int fb = fd(t.y); if (fa!=fb) { sum += 1LL*sz[fa]*sz[fb]; p[fa] = fb; sz[fb] += sz[fa]; if (mx1[fa]>=mx1[fb]) mx2[fb] = max(mx1[fb], mx2[fa]), mx1[fb] = mx1[fa]; else if (mx1[fa]>mx2[fb]) mx2[fb] = mx1[fa]; if (mi1[fa]<=mi1[fb]) mi2[fb] = min(mi1[fb], mi2[fa]), mi1[fb] = mi1[fa]; else if (mi1[fa]<mi2[fb]) mi2[fb] = mi1[fa]; ans = max({ans, 1LL*mx1[fb]*mx2[fb], 1LL*mi1[fb]*mi2[fb]}); } } } int main() { IOS; cin >> n, m = 122; cin >> s+1; get_sa(); get_h(); for (int i = 1; i<=n; ++i) { cin >> mx1[i]; mi1[i] = mx1[i]; mx2[i] = -2e18, mi2[i] = 2e18; p[i] = i, sz[i] = 1; } vector<P> t; for (int i = n-1; i>=0; --i) { calc(i); t.push_back({sum, ans<-1.5e18 ? 0:ans}); } for (int i = t.size()-1; i>=0; --i) cout << t[i].x << ' ' << t[i].y << endl; return 0; }