[NOI2015]品酒大会 - 后缀自动机

建出SAM和后缀树

两个子串的最长lcp就是他们的lca

因此我们只需求出最长的个数然后用前缀和就能算出总个数,最大值也是同样的方法, 当然要逆序建SAM才能保证他们的第一位相同。

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 #define LL long long
  6 
  7 using namespace std;
  8 
  9 inline LL read()
 10 {
 11     LL x = 0, w = 1; char ch = 0;
 12     while(ch < '0' || ch > '9') {
 13         if(ch == '-') {
 14             w = -1;
 15         }
 16         ch = getchar();
 17     }
 18     while(ch >= '0' && ch <= '9') {
 19         x = x * 10 + ch - '0';
 20         ch = getchar();
 21     }
 22     return x * w;
 23 }
 24 
 25 const int MAXN = 1e6 + 10;
 26 
 27 int head[MAXN];
 28 char s[MAXN];
 29 LL d[MAXN];
 30 LL ans[MAXN], num[MAXN];
 31 LL fi, se;
 32 int flag[MAXN];
 33 LL size[MAXN];
 34 LL maxn[MAXN], minn[MAXN];
 35 int len;
 36 int lst = 1, tot = 1;
 37 
 38 struct SAM {
 39     int c[27];
 40     int fa, len;
 41 } t[MAXN];
 42 
 43 void extend(int x, int d)
 44 {
 45     int last = lst, np = ++tot;
 46     t[np].len = t[last].len + 1;
 47     size[np] = 1;
 48     lst = np;
 49     maxn[np] = minn[np] = d;
 50     while(last && !t[last].c[x]) {
 51         t[last].c[x] = np;
 52         last = t[last].fa; 
 53     }
 54     if(!last) {
 55         t[np].fa = 1;
 56     } else {
 57         int q = t[last].c[x];
 58         if(t[q].len == t[last].len + 1) {
 59             t[np].fa = q;
 60         } else {
 61             int nq = ++tot;
 62             flag[nq] = 1;
 63             maxn[nq] = -2e18, minn[nq] = 2e18;
 64             t[nq] = t[q];
 65             t[nq].len = t[last].len + 1;
 66             t[np].fa = t[q].fa = nq;
 67             while(t[last].c[x] == q) {
 68                 t[last].c[x] = nq;
 69                 last = t[last].fa;
 70             }
 71         }
 72     }
 73 }
 74 
 75 int cnt = 0;
 76 
 77 struct edge {
 78     int v, next;
 79 } g[MAXN];
 80 
 81 void addedge(int u, int v)
 82 {
 83     g[++cnt].v = v;
 84     g[cnt].next = head[u];
 85     head[u] = cnt;    
 86 }
 87 
 88 bool ok(int x)
 89 {
 90     return maxn[x] != -2e18 && minn[x] != 2e18;
 91 }
 92 
 93 void DFS(int x)
 94 {    
 95     for(int j = head[x]; j; j = g[j].next) {
 96         int to = g[j].v;
 97         DFS(to);
 98         num[t[x].len] += size[x] * size[to];
 99         size[x] += size[to];
100         if(ok(x) && ok(to)) {
101             ans[t[x].len] = max(ans[t[x].len], max(maxn[x] * maxn[to], minn[x] * minn[to]));
102         }
103         maxn[x] = max(maxn[x], maxn[to]);
104         minn[x] = min(minn[x], minn[to]);
105     }
106 }
107 
108 int main()
109 {
110     //freopen("drink.out", "w", stdout);
111     len = read();
112     scanf("%s", s + 1);
113     fi = -2e18, se = 2e18;
114     maxn[1] = -2e18, minn[1] = 2e18;
115     for(int i = 1; i <= len; i++) {
116         d[i] = read();
117         if(fi != -2e18)
118         ans[0] = max(fi * d[i], ans[0]);
119         if(se != 2e18)
120         ans[0] = max(se * d[i], ans[0]);
121         fi = max(fi, d[i]), se = min(se, d[i]);
122     }
123     for(int i = 0; i <= len; i++) {
124         ans[i] = -2e18;
125     }
126     for(int i = len; i >= 1; i--) {
127         extend(s[i] - 'a', d[i]);
128     }
129     for(int i = 2; i <= tot; i++) {
130         addedge(t[i].fa, i);
131     }
132     DFS(1);
133     for(int i = len - 1; i >= 0; i--) {
134         num[i] += num[i + 1];
135         ans[i] = max(ans[i], ans[i + 1]);
136     }
137     for(int i = 0; i < len; i++) {
138         if(num[i] == 0) {
139             printf("0 0\n");
140         } else {
141             printf("%lld %lld\n", num[i], ans[i]);
142         }
143     }
144     return 0;
145 }
View Code

 

posted @ 2018-07-02 21:40  大财主  阅读(214)  评论(0编辑  收藏  举报