bzoj4199: [Noi2015]品酒大会 (并查集 && 后缀数组)
据说用后缀自动机 + dp也能做
然而并不会
后缀数组的做法呢
就是先建个后缀数组,求出height值,此时如果直接找,复杂度是n ^ 2的,肯定会超时。
但是height大的值是不会对小的产生影响的,所以可以按height大小,从大到小合并两个区间,用并查集维护就可以了
代码如下
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 const int N = 300010; 6 typedef long long ll; 7 char s[N]; 8 ll a[N]; 9 int n; 10 int sa[N], c[N], x[N], y[N]; 11 12 struct E { 13 int h, l, r; 14 inline bool operator < (const E o) const { 15 return h > o.h; 16 } 17 } g[N]; 18 inline void BuildSa(int m) { 19 for (int i = 0; i < m; i++) c[i] = 0; 20 for (int i = 0; i < n; i++) c[x[i] = s[i]]++; 21 for (int i = 1; i < m; i++) c[i] += c[i - 1]; 22 for (int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; 23 for (int k = 1; k <= n; k <<= 1) { 24 int p = 0; 25 for (int i = n - k; i < n; i++) y[p++] = i; 26 for (int i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k; 27 for (int i = 0; i < m; i++) c[i] = 0; 28 for (int i = 0; i < n; i++) c[x[y[i]]]++; 29 for (int i = 1; i < m; i++) c[i] += c[i - 1]; 30 for (int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; 31 p = 1; swap(x, y); 32 x[sa[0]] = 0; 33 for (int i = 1; i < n; i++) 34 x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1 : p++; 35 if (p >= n) break; 36 m = p; 37 } 38 } 39 40 int height[N], rank[N]; 41 inline void GetHeight() { 42 for (int i = 0; i < n; i++) rank[sa[i]] = i; 43 int k = 0; 44 for (int i = 0; i < n; i++) { 45 if (k) k--; 46 int j = sa[rank[i] - 1]; 47 while (s[j + k] == s[i + k]) k++; 48 height[rank[i]] = k; 49 } 50 } 51 52 ll fa[N], size[N], ans[N], ans1[N], ans2[N], minn[N], maxn[N]; 53 void Union(int x, int y) { 54 fa[x] = y; 55 size[y] += size[x]; 56 ans[y] = max(ans[y], max(maxn[y] * maxn[x], minn[y] * minn[x])); 57 maxn[y] = max(maxn[y], maxn[x]); 58 minn[y] = min(minn[y], minn[x]); 59 } 60 61 int find(int x) { 62 return (fa[x] == x) ? x : find(fa[x]); 63 } 64 65 int main() { 66 scanf("%d", &n); 67 getchar(); 68 for (int i = 0; i < n; i++) 69 scanf("%c", &s[i]), s[i] -= 'a' - 1; 70 for (int i = 0; i < n; i++) 71 scanf("%lld", &a[i]); 72 s[n++] = 0; 73 BuildSa(30); 74 GetHeight(); 75 for (int i = 2; i < n; i++) { 76 g[i].h = height[i]; 77 g[i].l = sa[i]; 78 g[i].r = sa[i - 1]; 79 } 80 sort(g + 2, g + n); 81 for (int i = 0; i < n; i++) fa[i] = i, size[i] = 1, maxn[i] = a[i], minn[i] = a[i]; 82 memset(ans, 0x80, sizeof(ans)); 83 memset(ans2, 0x80, sizeof(ans2)); 84 for (int i = 2; i < n; i++) { 85 int x = find(g[i].l); 86 int y = find(g[i].r); 87 ans1[g[i].h] += (ll)size[x] * size[y]; 88 Union(x, y); 89 ans2[g[i].h] = max(ans2[g[i].h], ans[y]); 90 } 91 for (int i = n - 3; i >= 0; i--) { 92 ans1[i] += ans1[i + 1]; 93 if (ans1[i + 1]) ans2[i] = max(ans2[i], ans2[i + 1]); 94 } 95 for (int i = 0; i < n - 1; i++) printf("%lld %lld\n", ans1[i], ans1[i] ? ans2[i] : 0); 96 return 0; 97 }