[NOI 2015] 品酒大会
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=4199
[算法]
首先 , 题目中有一条性质 :
若两个子串是“r相似”的 , 那么它们同样是(r - 1)相似 ,(r - 2)相似 , ... , 0相似的
不妨考虑构建给定字符串的后缀数组 , 将所有后缀按height值排序 , 用并查集合并答案
并查集维护集合的大小 , 集合内的最大 , 次大权值与最小 , 次小权值
每次合并两个集合x , y , 假设它们最多是"z相似"的
那么就会对ans1[0..z]产生size(x) * size(y)的贡献
同样会对ans2[0..2]产生max{ max1 * max2 , min1 * min2 }的贡献
用差分前缀和求出ans1 , 后缀最值求出ans2即可
时间复杂度 : O(NlogN)
[代码]
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int N = 3e5 + 10; const ll inf = 1e18; struct info { int x , y; } a[N]; int n; int rk[N] , cnt[N] , sa[N] , height[N] , fa[N] , sz[N]; ll max1[N] , max2[N] , min1[N] , min2[N] , w[N] , ans1[N] , ans2[N]; char s[N]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline void build_sa() { static int x[N] , y[N] , cnt[N]; for (int i = 1; i <= n; ++i) ++cnt[s[i]]; for (int i = 1; i <= 256; ++i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; --i) sa[cnt[s[i]]--] = i; rk[sa[1]] = 1; for (int i = 2; i <= n; ++i) rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]); for (int k = 1; rk[sa[n]] != n; k <<= 1) { for (int i = 1; i <= n; ++i) x[i] = rk[i] , y[i] = (i + k <= n) ? rk[i + k] : 0; memset(cnt , 0 , sizeof(cnt)); for (int i = 1; i <= n; ++i) ++cnt[y[i]]; for (int i = 1; i <= n; ++i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) rk[cnt[y[i]]--] = i; memset(cnt , 0 , sizeof(cnt)); for (int i = 1; i <= n; ++i) ++cnt[x[i]]; for (int i = 1; i <= n; ++i) cnt[i] += cnt[i - 1]; for (int i = n; i >= 1; i--) sa[cnt[x[rk[i]]]--] = rk[i]; rk[sa[1]] = 1; for (int i = 2; i <= n; ++i) rk[sa[i]] = rk[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]); } } inline void get_height() { int k = 0; for (int i = 1; i <= n; ++i) { int j = sa[rk[i] + 1]; if (k) --k; while (s[i + k] == s[j + k]) ++k; height[rk[i]] = k; } } inline bool cmp(info a , info b) { return a.y > b.y; } inline int get_root(int x) { if (fa[x] == x) return x; else return fa[x] = get_root(fa[x]); } inline void merge(int x , int y) { ll A[5] = {0 , max1[x] , max2[x] , max1[y] , max2[y]} , B[5] = {0 , min1[x] , min2[x] , min1[y] , min2[y]}; if (sz[x] > sz[y]) swap(x , y); sz[y] += sz[x]; fa[x] = y; sort(A + 1 , A + 5 , greater<ll>()); sort(B + 1 , B + 5); max1[y] = A[1]; max2[y] = A[2]; min1[y] = B[1]; min2[y] = B[2]; return; } int main() { scanf("%d" , &n); scanf("%s" , s + 1); for (int i = 1; i <= n; ++i) scanf("%lld" , &w[i]); build_sa(); get_height(); for (int i = 1; i < n; ++i) { a[i].x = i; a[i].y = height[i]; } for (int i = 1; i <= n; ++i) { fa[i] = i; sz[i] = 1; max1[i] = w[sa[i]] , max2[i] = -inf; min1[i] = w[sa[i]] , min2[i] = inf; ans2[i] = -inf; } ans2[0] = -inf; sort(a + 1 , a + n , cmp); for (int i = 1; i < n; ++i) { int rk = a[i].x , ht = a[i].y; int fx = get_root(rk) , fy = get_root(rk + 1); ans1[0] += 1ll * sz[fx] * sz[fy]; ans1[ht + 1] -= 1ll * sz[fx] * sz[fy]; merge(fx , fy); fx = get_root(rk); chkmax(ans2[ht] , max(1ll * max1[fx] * max2[fx] , 1ll * min1[fx] * min2[fx])); } for (int i = 1; i < n; ++i) ans1[i] += ans1[i - 1]; for (int i = n - 1; i >= 0; --i) ans2[i] = max(ans2[i + 1] , ans2[i]); for (int i = 0; i < n; ++i) if (!ans1[i]) puts("0 0"); else printf("%lld %lld\n" , ans1[i] , ans2[i]); return 0; }