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 }

 

posted @ 2018-02-22 10:30  cminus  阅读(201)  评论(0编辑  收藏  举报