题意:求一个字符串中有多少形如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 = ;
  7.  
  8. char str[N];
  9. int pos[N], pos2[N], pw[N * ], n;
  10.  
  11. struct SAM {
  12.  
  13. struct Edge {
  14. int nex, v;
  15. }edge[N]; int top;
  16.  
  17. int tr[N][], fail[N], len[N], tot, last;
  18. int ST[N * ][], pos[N * ], num, e[N], d[N];
  19.  
  20. SAM() {
  21. tot = last = ;
  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] + ;
  37. while(p && !tr[p][f]) {
  38. tr[p][f] = np;
  39. p = fail[p];
  40. }
  41. if(!p) {
  42. fail[np] = ;
  43. }
  44. else {
  45. int Q = tr[p][f];
  46. if(len[Q] == len[p] + ) {
  47. fail[np] = Q;
  48. }
  49. else {
  50. int nQ = ++tot;
  51. len[nQ] = len[p] + ;
  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][] = x;
  66. for(int i = e[x]; i; i = edge[i].nex) {
  67. int y = edge[i].v;
  68. d[y] = d[x] + ;
  69. DFS(y);
  70. ST[++num][] = x;
  71. }
  72. return;
  73. }
  74.  
  75. inline void prework() {
  76. for(int i = ; i <= tot; i++) {
  77. add(fail[i], i);
  78. }
  79. d[] = ;
  80. DFS();
  81. for(int j = ; j <= pw[num]; j++) {
  82. for(int i = ; i + ( << j) - <= num; i++) {
  83. if(d[ST[i][j - ]] <= d[ST[i + ( << (j - ))][j - ]]) {
  84. ST[i][j] = ST[i][j - ];
  85. }
  86. else {
  87. ST[i][j] = ST[i + ( << (j - ))][j - ];
  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 + ];
  101. if(d[ST[x][t]] <= d[ST[y - ( << t) + ][t]]) {
  102. return ST[x][t];
  103. }
  104. return ST[y - ( << t) + ][t];
  105. }
  106.  
  107. inline void clear() {
  108. for(int i = ; i <= tot; i++) {
  109. d[i] = e[i] = ;
  110. for(int f = ; f < ; f++) {
  111. tr[i][f] = ;
  112. }
  113. }
  114. tot = last = ;
  115. top = num = ;
  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 * ];
  127. int f[N];
  128. inline void pushdown(int o) {
  129. if(!tag[o]) {
  130. return;
  131. }
  132. tag[o << ] += tag[o];
  133. tag[o << | ] += tag[o];
  134. tag[o] = ;
  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) >> ;
  144. pushdown(o);
  145. if(L <= mid) {
  146. add(L, R, l, mid, o << );
  147. }
  148. if(mid < R) {
  149. add(L, R, mid + , r, o << | );
  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) >> ;
  161. solve(l, mid, o << );
  162. solve(mid + , r, o << | );
  163. return;
  164. }
  165. void clear(int l, int r, int o) {
  166. tag[o] = ;
  167. if(l == r) {
  168. return;
  169. }
  170. int mid = (l + r) >> ;
  171. clear(l, mid, o << );
  172. clear(mid + , r, o << | );
  173. return;
  174. }
  175. }seg, seg2;
  176.  
  177. inline void solve() {
  178. scanf("%s", str);
  179. LL ans = ;
  180. n = strlen(str);
  181. for(int i = ; i < n; i++) {
  182. sam.insert(str[i]);
  183. sam2.insert(str[n - i - ]);
  184. pos[i] = sam.last;
  185. pos2[n - i - ] = sam2.last;
  186. }
  187. sam.prework();
  188. sam2.prework();
  189. //
  190. for(int len = ; (len << ) < n - ; 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 + , i - len * + y + , , n, );
  201. //printf(" > > > 1 add %d %d \n", i - len - x + 2, i - len * 2 + y + 1);
  202. seg2.add(i + len - x + , i + y, , n, );
  203. //printf(" > > > 2 add %d %d \n", i + len - x + 1, i + y);
  204. }
  205. }
  206. }
  207. seg.solve(, n, );
  208. seg2.solve(, n, );
  209. for(int i = ; i < n - ; i++) {
  210. ans += 1ll * seg2.f[i] * seg.f[i + ];
  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 = ; i < N * ; i++) {
  220. pw[i] = pw[i >> ] + ;
  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(, n, );
  230. seg2.clear(, n, );
  231. }
  232. }
  233. return ;
  234. }

AC代码

洛谷P1117 优秀的拆分的更多相关文章

  1. 洛谷P1117 优秀的拆分【Hash】【字符串】【二分】【好难不会】

    题目描述 如果一个字符串可以被拆分为AABBAABB的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串aabaabaaaabaabaa,如果令 A=aabA ...

  2. bzoj 4650 & 洛谷 P1117 优秀的拆分 —— 枚举关键点+后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 ...

  3. 洛谷 P2404 自然数的拆分问题

    题目链接 https://www.luogu.org/problemnew/show/P2404 题目背景 木有...... 题目描述 任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和. ...

  4. 洛谷P1117 棋盘游戏

    洛谷1117 棋盘游戏 题目描述 在一个4*4的棋盘上有8个黑棋和8个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的.移动棋子的规则是交换相邻两个棋子.现在给出一个初始棋盘和一个最终棋盘,要 ...

  5. 洛谷——P2404 自然数的拆分问题

    题目背景 任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和. 题目描述 任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和. 输入输出格式 输入格式: 输入:待拆分的自然数n ...

  6. 【洛谷1117_BZOJ4650】[NOI2016] 优秀的拆分(哈希_后缀数组_RMQ)

    题目: 洛谷1117 分析: 定义把我校某兔姓神犇Tzz和他的妹子拆分,为"优秀的拆分" 随便写个哈希就能有\(95\)分的好成绩-- 我的\(95\)分做法比fei较chang奇 ...

  7. 洛谷4451 整数的lqp拆分(生成函数)

    比较水的一题.居然是一道没看题解就会做的黑题…… 题目链接:洛谷 题目大意:定义一个长度为 $m$ 的正整数序列 $a$ 的价值为 $\prod f_{a_i}$.($f$ 是斐波那契数)对于每一个 ...

  8. 洛谷:P3281 [SCOI2013]数数 (优秀的解法)

    刷了这么久的数位 dp ,照样被这题虐,还从早上虐到晚上,对自己无语...(机房里又是只有我一个人,寂寞.) 题目:洛谷P3281 [SCOI2013]数数 题目描述 Fish 是一条生活在海里的鱼, ...

  9. P1117 [NOI2016]优秀的拆分

    $ \color{#0066ff}{ 题目描述 }$ 如果一个字符串可以被拆分为\(AABB\)的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aab ...

随机推荐

  1. JavaMail入门第一篇 邮件简介及API概述

    现如今,电子邮件在我们的生活当中扮演着越来越重要的角色,我们每个人几乎都会与其打交道(至少时不时我们都会接收到莫名其妙的垃圾邮件),在工作中,使用邮件进行交流沟通,可以使我们的工作有迹可循,也显的较为 ...

  2. 深浅copy详解

    一. 前言 在python中,对象的赋值和深浅copy,是有差异的.最终得的值也不同,下面我们就通过几个例子,来看下它们之间的区别. 二. 赋值 list2 = ["jack",2 ...

  3. flutter image_picker使用照相机

    dependencies: image_picker: ^0.4.12+1 最新的^0.5+9编译无法通过 import 'dart:io'; import 'dart:async'; import ...

  4. HTML5开发之meta标签的viewport使用说明

    随着高端手机(Andriod,Iphone,Ipod,WinPhone等)的盛行,移动互联应用开发也越来越受到人们的重视,用html5开发移动应用是最好的选择.然而每一款手机有不同的分辨率,不同屏幕大 ...

  5. Python魔法方法(magic method)细解几个常用魔法方法(上)

    这里只分析几个可能会常用到的魔法方法,像__new__这种不常用的,用来做元类初始化的或者是__init__这种初始化使用的 每个人都会用的就不介绍了. 其实每个魔法方法都是在对内建方法的重写,和做像 ...

  6. 集合转数组的toArray()和toArray(T[] a)方法

    参考:集合转数组的toArray()和toArray(T[] a)方法 1.ArrayList的toArray ArrayList提供了一个将List转为数组的一个非常方便的方法toArray.toA ...

  7. falsk 项目中日志设置

    app/__init__.py: 1 import logging from logging.handlers import RotatingFileHandler ''' 开发中使用DEBUG级别, ...

  8. C#中decimal,double和float的区别

    float 单精度浮点 32bit,double 双精度浮点64bit,decimal是高精度 128bit,浮点型.float double 是 基本类型(primitive type),decim ...

  9. nginx 负载均衡(默认算法)

    使用 nginx 的upstream模块只需要几步就可以实现一个负载均衡: 在 nginx 配置文件中添加两个server server { listen ; server_name 192.168. ...

  10. hdu-2072(字典树)

    字典树模板题 代码 #include<iostream> #include<algorithm> #include<cstdio> #include<cstr ...