洛谷P1117 优秀的拆分

题意:求一个字符串中有多少形如AABB的子串。

解:嗯...我首先极度SB的想了一个后缀自动机套线段树启发式合并的做法,想必会TLE。

然后跑去看题解,发现实在是妙不可言...

显然要对每个位置求出向左有多少个AA,向右有多少个BB。

我的想法是对于每个前缀,两两求lca,如果lca的len大于他们的位置之差,显然就有一组了。

这时候把贡献加到其中较长的前缀上。然后反着来一遍就行了。

怎么批量求lca和贡献呢?

考虑计算每个点作为lca时的贡献,显然线段树维护子树内有哪些前缀。合并的时候好像没啥好的办法...但是我们有启发式合并!

每次取出小的线段树中的所有元素,依次加入大的线段树中。对于大的线段树中比它小的一段区间内的元素,我们要给它自己加上贡献。对于比它大的一段区间中的元素,要给那些大的元素每个+1贡献。我们就在每次需要插入元素的时候往下推。推到底的时候加贡献即可。(应该支持吧...)

比较菜没写代码...感觉实现起来毒瘤的紧。

然后说正解。

考虑枚举AA串的长度。

对于一个长为2len的AA串,如果我们每隔len放一个点,那么这样的串将会且仅会覆盖两个连续的点。

对于每两个连续的点,我们求它们的最长公共前/后缀长度,分别设为x,y。

如果x + y >= len的话就是存在这样的AA串经过这两点。然后就是个线段树区间+1

最后遍历线段树统计答案即可。

求lcp不就是SAM的fail树上lca嘛,我会倍增!

Tnlog2n成功T飞...

然后就O(1)lca过了...果然O(1)lca还是有用的。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 typedef long long LL;
  6 const int N = 80010;
  7 
  8 char str[N];
  9 int pos[N], pos2[N], pw[N * 2], n;
 10 
 11 struct SAM {
 12 
 13     struct Edge {
 14         int nex, v;
 15     }edge[N]; int top;
 16     
 17     int tr[N][26], fail[N], len[N], tot, last;
 18     int ST[N * 2][20], pos[N * 2], num, e[N], d[N];
 19     
 20     SAM() {
 21         tot = last = 1;
 22     }
 23 
 24     inline void add(int x, int y) {
 25         top++;
 26         edge[top].v = y;
 27         edge[top].nex = e[x];
 28         e[x] = top;
 29         return;
 30     }
 31 
 32     inline void insert(char c) {
 33         int f = c - 'a';
 34         int p = last, np = ++tot;
 35         last = np;
 36         len[np] = len[p] + 1;
 37         while(p && !tr[p][f]) {
 38             tr[p][f] = np;
 39             p = fail[p];
 40         }
 41         if(!p) {
 42             fail[np] = 1;
 43         }
 44         else {
 45             int Q = tr[p][f];
 46             if(len[Q] == len[p] + 1) {
 47                 fail[np] = Q;
 48             }
 49             else {
 50                 int nQ = ++tot;
 51                 len[nQ] = len[p] + 1;
 52                 fail[nQ] = fail[Q];
 53                 fail[Q] = fail[np] = nQ;
 54                 memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
 55                 while(tr[p][f] == Q) {
 56                     tr[p][f] = nQ;
 57                     p = fail[p];
 58                 }
 59             }
 60         }
 61     }
 62     
 63     void DFS(int x) {
 64         pos[x] = ++num;
 65         ST[num][0] = x;
 66         for(int i = e[x]; i; i = edge[i].nex) {
 67             int y = edge[i].v;
 68             d[y] = d[x] + 1;
 69             DFS(y);
 70             ST[++num][0] = x;
 71         }
 72         return;
 73     }
 74 
 75     inline void prework() {
 76         for(int i = 2; i <= tot; i++) {
 77             add(fail[i], i);
 78         }
 79         d[1] = 1;
 80         DFS(1);
 81         for(int j = 1; j <= pw[num]; j++) {
 82             for(int i = 1; i + (1 << j) - 1 <= num; i++) {
 83                 if(d[ST[i][j - 1]] <= d[ST[i + (1 << (j - 1))][j - 1]]) {
 84                     ST[i][j] = ST[i][j - 1];
 85                 }
 86                 else {
 87                     ST[i][j] = ST[i + (1 << (j  - 1))][j - 1];
 88                 }
 89             }
 90         }
 91         return;
 92     }
 93 
 94     inline int lca(int x, int y) {
 95         x = pos[x];
 96         y = pos[y];
 97         if(x > y) {
 98             std::swap(x, y);
 99         }
100         int t = pw[y - x + 1];
101         if(d[ST[x][t]] <= d[ST[y - (1 << t) + 1][t]]) {
102             return ST[x][t];
103         }
104         return ST[y - (1 << t) + 1][t];
105     }
106 
107     inline void clear() {
108         for(int i = 1; i <= tot; i++) {
109             d[i] = e[i] = 0;
110             for(int f = 0; f < 26; f++) {
111                 tr[i][f] = 0;
112             }
113         }
114         tot = last = 1;
115         top = num = 0;
116         return;
117     }
118 
119     inline int lcp(int x, int y) {
120         return std::min(std::min(len[x], len[y]), len[lca(x, y)]);
121     }
122 
123 }sam, sam2;
124 
125 struct SegmentTree {
126     int tag[N * 2];
127     int f[N];
128     inline void pushdown(int o) {
129         if(!tag[o]) {
130             return;
131         }
132         tag[o << 1] += tag[o];
133         tag[o << 1 | 1] += tag[o];
134         tag[o] = 0;
135         return;
136     }
137 
138     void add(int L, int R, int l, int r, int o) {
139         if(L <= l && r <= R) {
140             tag[o]++;
141             return;
142         }
143         int mid = (l + r) >> 1;
144         pushdown(o);
145         if(L <= mid) {
146             add(L, R, l, mid, o << 1);
147         }
148         if(mid < R) {
149             add(L, R, mid + 1, r, o << 1 | 1);
150         }
151         return;
152     }
153 
154     void solve(int l, int r, int o) {
155         if(l == r) {
156             f[r] = tag[o];
157             return;
158         }
159         pushdown(o);
160         int mid = (l + r) >> 1;
161         solve(l, mid, o << 1);
162         solve(mid + 1, r, o << 1 | 1);
163         return;
164     }
165     void clear(int l, int r, int o) {
166         tag[o] = 0;
167         if(l == r) {
168             return;
169         }
170         int mid = (l + r) >> 1;
171         clear(l, mid, o << 1);
172         clear(mid + 1, r, o << 1 | 1);
173         return;
174     }
175 }seg, seg2;
176 
177 inline void solve() {
178     scanf("%s", str);
179     LL ans = 0;
180     n = strlen(str);
181     for(int i = 0; i < n; i++) {
182         sam.insert(str[i]);
183         sam2.insert(str[n - i - 1]);
184         pos[i] = sam.last;
185         pos2[n - i - 1] = sam2.last;
186     }
187     sam.prework();
188     sam2.prework();
189     //
190     for(int len = 1; (len << 1) < n - 1; len++) {
191         //printf("len = %d \n", len);
192         for(int i = len; i < n; i += len) {
193             // i i-len
194             //printf(" > %d %d \n", i - len, i);
195             int x = std::min(len, sam.lcp(pos[i], pos[i - len]));
196             int y = std::min(len, sam2.lcp(pos2[i], pos2[i - len]));
197             // x + y - len
198             //printf(" > x = %d y = %d \n", x, y);
199             if(x + y > len) {
200                 seg.add(i - len - x + 2, i - len * 2 + y + 1, 1, n, 1);
201                 //printf(" > > > 1 add %d %d \n", i - len - x + 2, i - len * 2 + y + 1);
202                 seg2.add(i + len - x + 1, i + y, 1, n, 1);
203                 //printf(" > > > 2 add %d %d \n", i + len - x + 1, i + y);
204             }
205         }
206     }
207     seg.solve(1, n, 1);
208     seg2.solve(1, n, 1);
209     for(int i = 2; i < n - 1; i++) {
210         ans += 1ll * seg2.f[i] * seg.f[i + 1];
211         //printf("ans += %d * %d \n", seg2.f[i], seg.f[i + 1]);
212     }
213     printf("%lld\n", ans);
214     return;
215 }
216 
217 int main() {
218 
219     for(int i = 2; i < N * 2; i++) {
220         pw[i] = pw[i >> 1] + 1;
221     }
222     int T;
223     scanf("%d", &T);
224     while(T--) {
225         solve();
226         if(T) {
227             sam.clear();
228             sam2.clear();
229             seg.clear(1, n, 1);
230             seg2.clear(1, n, 1);
231         }
232     }
233     return 0;
234 }
AC代码

 

posted @ 2019-02-01 22:23  huyufeifei  阅读(375)  评论(1编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜