题意:求一个字符串中有多少形如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还是有用的。

 #include <cstdio>
#include <cstring>
#include <algorithm> typedef long long LL;
const int N = ; char str[N];
int pos[N], pos2[N], pw[N * ], n; struct SAM { struct Edge {
int nex, v;
}edge[N]; int top; int tr[N][], fail[N], len[N], tot, last;
int ST[N * ][], pos[N * ], num, e[N], d[N]; SAM() {
tot = last = ;
} inline void add(int x, int y) {
top++;
edge[top].v = y;
edge[top].nex = e[x];
e[x] = top;
return;
} inline void insert(char c) {
int f = c - 'a';
int p = last, np = ++tot;
last = np;
len[np] = len[p] + ;
while(p && !tr[p][f]) {
tr[p][f] = np;
p = fail[p];
}
if(!p) {
fail[np] = ;
}
else {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
fail[np] = Q;
}
else {
int nQ = ++tot;
len[nQ] = len[p] + ;
fail[nQ] = fail[Q];
fail[Q] = fail[np] = nQ;
memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
while(tr[p][f] == Q) {
tr[p][f] = nQ;
p = fail[p];
}
}
}
} void DFS(int x) {
pos[x] = ++num;
ST[num][] = x;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
d[y] = d[x] + ;
DFS(y);
ST[++num][] = x;
}
return;
} inline void prework() {
for(int i = ; i <= tot; i++) {
add(fail[i], i);
}
d[] = ;
DFS();
for(int j = ; j <= pw[num]; j++) {
for(int i = ; i + ( << j) - <= num; i++) {
if(d[ST[i][j - ]] <= d[ST[i + ( << (j - ))][j - ]]) {
ST[i][j] = ST[i][j - ];
}
else {
ST[i][j] = ST[i + ( << (j - ))][j - ];
}
}
}
return;
} inline int lca(int x, int y) {
x = pos[x];
y = pos[y];
if(x > y) {
std::swap(x, y);
}
int t = pw[y - x + ];
if(d[ST[x][t]] <= d[ST[y - ( << t) + ][t]]) {
return ST[x][t];
}
return ST[y - ( << t) + ][t];
} inline void clear() {
for(int i = ; i <= tot; i++) {
d[i] = e[i] = ;
for(int f = ; f < ; f++) {
tr[i][f] = ;
}
}
tot = last = ;
top = num = ;
return;
} inline int lcp(int x, int y) {
return std::min(std::min(len[x], len[y]), len[lca(x, y)]);
} }sam, sam2; struct SegmentTree {
int tag[N * ];
int f[N];
inline void pushdown(int o) {
if(!tag[o]) {
return;
}
tag[o << ] += tag[o];
tag[o << | ] += tag[o];
tag[o] = ;
return;
} void add(int L, int R, int l, int r, int o) {
if(L <= l && r <= R) {
tag[o]++;
return;
}
int mid = (l + r) >> ;
pushdown(o);
if(L <= mid) {
add(L, R, l, mid, o << );
}
if(mid < R) {
add(L, R, mid + , r, o << | );
}
return;
} void solve(int l, int r, int o) {
if(l == r) {
f[r] = tag[o];
return;
}
pushdown(o);
int mid = (l + r) >> ;
solve(l, mid, o << );
solve(mid + , r, o << | );
return;
}
void clear(int l, int r, int o) {
tag[o] = ;
if(l == r) {
return;
}
int mid = (l + r) >> ;
clear(l, mid, o << );
clear(mid + , r, o << | );
return;
}
}seg, seg2; inline void solve() {
scanf("%s", str);
LL ans = ;
n = strlen(str);
for(int i = ; i < n; i++) {
sam.insert(str[i]);
sam2.insert(str[n - i - ]);
pos[i] = sam.last;
pos2[n - i - ] = sam2.last;
}
sam.prework();
sam2.prework();
//
for(int len = ; (len << ) < n - ; len++) {
//printf("len = %d \n", len);
for(int i = len; i < n; i += len) {
// i i-len
//printf(" > %d %d \n", i - len, i);
int x = std::min(len, sam.lcp(pos[i], pos[i - len]));
int y = std::min(len, sam2.lcp(pos2[i], pos2[i - len]));
// x + y - len
//printf(" > x = %d y = %d \n", x, y);
if(x + y > len) {
seg.add(i - len - x + , i - len * + y + , , n, );
//printf(" > > > 1 add %d %d \n", i - len - x + 2, i - len * 2 + y + 1);
seg2.add(i + len - x + , i + y, , n, );
//printf(" > > > 2 add %d %d \n", i + len - x + 1, i + y);
}
}
}
seg.solve(, n, );
seg2.solve(, n, );
for(int i = ; i < n - ; i++) {
ans += 1ll * seg2.f[i] * seg.f[i + ];
//printf("ans += %d * %d \n", seg2.f[i], seg.f[i + 1]);
}
printf("%lld\n", ans);
return;
} int main() { for(int i = ; i < N * ; i++) {
pw[i] = pw[i >> ] + ;
}
int T;
scanf("%d", &T);
while(T--) {
solve();
if(T) {
sam.clear();
sam2.clear();
seg.clear(, n, );
seg2.clear(, n, );
}
}
return ;
}

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. linux audit审计(3)--audit服务配置

    audit守护进程可以通过/etc/audit/auditd.conf文件进行配置,默认的auditd配置文件可以满足大多数环境的要求. local_events = yes write_logs = ...

  2. 从Oracle数据库中查询前几个月数据时需要注意的一些问题

    在最近的一个项目中,有一个需求就是要查询数据库中前几个月的历史数据,但是由于自己考虑不全面造成了程序的bug,现在将这一块好好作一个总结,希望以后不再犯这种很低级的错误,首先贴出查询中用到的一个子函数 ...

  3. BugFree 安装

    BugFree基于PHP和MySQL开发,是免费且开发源代码的缺陷管理系统.服务器端在Linux和Windows平台上都可以运行:客户端无需安装任何软件,通过IE,FireFox等浏览器就可以自由使用 ...

  4. jdbc 接口的用法 Statement和PreparedStatement的区别!

    package cn.zhouzhou; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Resu ...

  5. Javassist之常用API的应用 02

    测试模型代码: package org.study2.JavaSenior.annotation.javassistDemo; /** * @Auther:GongXingRui * @Date:20 ...

  6. Python——Flask框架——模板

    一.渲染模板 render_template 函数把Jinja2模板引擎集成到程序中 二.Jinja2变量过滤器 过滤器名 说明 safe 渲染值是不转义 capitalize 把值得首字母转换成大写 ...

  7. Lodop打印较大的超出纸张的图片

    ADD_PRINT_IMAGE打印图片时,如果一个图片过大,超出纸张,默认超出部分是不显示的,也不会分页.最近遇到有人利用ADD_PRINT_URL打印图片,说图片自动分了多页,因为这个方法一般是用来 ...

  8. Spring Boot 构建电商基础秒杀项目 (八) 商品创建

    SpringBoot构建电商基础秒杀项目 学习笔记 新建数据表 create table if not exists item ( id int not null auto_increment, ti ...

  9. Multiple websites on single instance of IIS

    序幕 通常需要在单个IIS实例上托管多个网站,主要在开发环境中,而不是在生产服务器上.我相信它在生产服务器上不是一个首选解决方案,但这至少是一个可能的实现. Web服务器单实例上的多个网站的好处是: ...

  10. Clear Linux 为脚本语言提供更高的性能

    导读 Clear Linux的领先性能不仅限于C/C++应用程序,而且PHP,R和Python等脚本语言也有很大的提升速度.在一篇新的博客文章中,英特尔的一位开发人员概述了他们对Python的一些性能 ...