LOJ#2083. 「NOI2016」优秀的拆分
$n \leq 30000$的字符串,问其所有子串的所有AABB形式的拆分有多少种。$t \leq 10$组询问。
$n^3$过80,$n^2$过95,鬼去写正解。。
$n^2$:先枚举一次算每个位置结尾的AA形式的子串,再枚举一次用类似的方法算答案。
正解:类似,记每个位置结尾的AA的子串和每个位置开头的即可。算这个数组可用如此方法:枚举A长度$L$,每A个位置标记一个关键点。然后相邻两个关键点$a,b$,找前缀$a,b$的最长公共后缀$p$和后缀$a,b$的最长公共前缀$s$,若$p+s>L$说明这里有一些A,其中作为起点的范围是$[a-p+1,a+s-L]$,作为终点的范围$[b-p+L,b+s-1]$,相当于做了个区间加,可以差分。这样做的话,复杂度就变成$\frac{n}{1}+\frac{n}{2}+...=n \ln n$了。
找最长公共前后缀,sa或二分hash或sam均可。
//#include<iostream>
#include<cstring>
#include<cstdio>
//#include<math.h>
//#include<set>
//#include<queue>
//#include<bitset>
//#include<vector>
#include<algorithm>
#include<stdlib.h>
using namespace std; #define LL long long
int qread()
{
char c; int s=,f=; while ((c=getchar())<'' || c>'') (c=='-') && (f=-);
do s=s*+c-''; while ((c=getchar())>='' && c<=''); return s*f;
} //Pay attention to '-' , LL and double of qread!!!! int T,n;
#define maxn 60011
char s[maxn]; int f[maxn],g[maxn]; struct SAM
{
struct Node{int pre,ch[],pos,Max;}a[maxn];
int size,last;
void clear()
{
size=last=; a[].pre=-; a[].pos=a[].Max=; memset(a[].ch,,sizeof(a[].ch));
le=; memset(first,,sizeof(first));
}
int pp[maxn];
void insert(int c,int p)
{
int x=++size,y=last; memset(a[x].ch,,sizeof(a[x].ch));
a[x].Max=a[last].Max+; a[x].pos=p; pp[p]=x;
last=x;
for (;~y && !a[y].ch[c];y=a[y].pre) a[y].ch[c]=x;
if (!~y) a[x].pre=;
else if (a[a[y].ch[c]].Max==a[y].Max+) a[x].pre=a[y].ch[c];
else
{
int z=a[y].ch[c],w=++size; a[w]=a[z]; a[w].pos=p; a[w].Max=a[y].Max+;
a[z].pre=a[x].pre=w;
for (;~y && a[y].ch[c]==z;y=a[y].pre) a[y].ch[c]=w;
}
}
struct Edge{int to,next;}edge[maxn<<]; int first[maxn],le;
void in(int x,int y) {Edge &e=edge[le]; e.to=y; e.next=first[x]; first[x]=le++;}
int Log[maxn<<],rmq[][maxn<<],dep[maxn],Time,id[maxn];
void predfs(int x)
{
rmq[][++Time]=x; id[x]=Time;
for (int i=first[x];i;i=edge[i].next)
{
Edge &e=edge[i]; dep[e.to]=dep[x]+;
predfs(e.to); rmq[][++Time]=x;
}
}
void pre()
{
for (int i=;i<=size;i++) in(a[i].pre,i);
Time=; predfs();
Log[]=-; for (int i=,to=size*-;i<=to;i++) Log[i]=Log[i>>]+;
for (int j=;j<=;j++)
for (int i=,to=size*-(<<j);i<=to;i++)
rmq[j][i]=dep[rmq[j-][i]]>dep[rmq[j-][i+(<<(j-))]]?rmq[j-][i+(<<(j-))]:rmq[j-][i];
}
int qq(int x,int y)
{
x=id[pp[x]]; y=id[pp[y]]; if (x>y) x^=y^=x^=y; int l=Log[y-x+];
return a[dep[rmq[l][x]]>dep[rmq[l][y-(<<l)+]]?rmq[l][y-(<<l)+]:rmq[l][x]].Max;
}
}s1,s2;
//1 shi zheng de , 2 shi fan de int main()
{
T=qread();
while (T--)
{
scanf("%s",s+); n=strlen(s+);
s1.clear(); s2.clear();
for (int i=;i<=n;i++) s1.insert(s[i]-'a',i);
for (int i=n;i;i--) s2.insert(s[i]-'a',i);
s1.pre(); s2.pre(); memset(f,,sizeof(f));
memset(g,,sizeof(g));
for (int i=;i<=n;i++)
for (int j=i;j<=n-i;j+=i)
{
int k=j+i,p=s1.qq(j,k),s=s2.qq(j,k); p=min(p,i); s=min(s,i);
if (p+s>i) f[j-p+]++,f[j+s-i+]--,g[k-p+i]++,g[k+s]--;
} for (int i=,now=;i<=n;i++) now+=f[i],f[i]=now;
for (int i=,now=;i<=n;i++) now+=g[i],g[i]=now;
LL ans=;
for (int i=;i<n;i++) ans+=g[i]*1ll*f[i+];
printf("%lld\n",ans);
}
return ;
}
LOJ#2083. 「NOI2016」优秀的拆分的更多相关文章
- 「NOI2016」优秀的拆分 解题报告
「NOI2016」优秀的拆分 这不是个SAM题,只是个LCP题目 95分的Hash很简单,枚举每个点为开头和末尾的AA串个数,然后乘一下之类的. 考虑怎么快速求"每个点为开头和末尾的AA串个 ...
- loj#2128. 「HAOI2015」数字串拆分 矩阵乘法
目录 题目链接 题解 代码 题目链接 loj#2128. 「HAOI2015」数字串拆分 题解 \(f(s)\)对于\(f(i) = \sum_{j = i - m}^{i - 1}f(j)\) 这个 ...
- LOJ#2086. 「NOI2016」区间
$n \leq 500000$个区间,从中挑出一些,使得至少有一个点被$m$个选中区间包含,且选中区间长度的极差最小. 区间题死脑筋晚期:把区间按左端点排序,然后右端点用个优先队列来弹,然后需要维护下 ...
- *LOJ#2085. 「NOI2016」循环之美
$n \leq 1e9,m \leq 1e9,k \leq 2000$,求$k$进制下$\frac{x}{y}$有多少种不同的纯循环数取值,$1 \leq x \leq n,1 \leq y \leq ...
- LOJ#2084. 「NOI2016」网格
$n,m \leq 1e9$,$n*m$的网格中有$c \leq 1e5$个是黑的,其他是白的.问:使至少两个白的不连通,最少需要再把几个白的涂黑. 可以发现答案是-1,0,1,2啦.-1要么没白的, ...
- Loj #2554. 「CTSC2018」青蕈领主
Loj #2554. 「CTSC2018」青蕈领主 题目描述 "也许,我的生命也已经如同风中残烛了吧."小绿如是说. 小绿同学因为微积分这门课,对"连续"这一概 ...
- Loj #2570. 「ZJOI2017」线段树
Loj #2570. 「ZJOI2017」线段树 题目描述 线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构.优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质. 最近可怜 ...
- UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
#219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...
- 【BZOJ4650】【NOI2016】优秀的拆分(后缀数组)
[BZOJ4650][NOI2016]优秀的拆分(后缀数组) 题面 BZOJ Uoj 题解 如果我们知道以某个位置为开始/结尾的\(AA\)串的个数 那就直接做一下乘法就好 这个怎么求? 枚举一个位置 ...
随机推荐
- 1072: [SCOI2007]排列perm
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3000 Solved: 1875[Submit][Status][Discuss] Descript ...
- mysql基础,修改数据表
- Centos 安装python 3.7 ,同时兼容python2.7
下载Python源码 从http://www.python.org/download/根据需要的版本下载源文件. 例如上图就是我在官网直接找到3.5.6版本的下载页面,点击的tar源码包进行下载. 1 ...
- 在生产环境下实现每天自动备份mysql数据库
1.描述 我相信很多朋友在工作都都会有这种需求,老板或领导让你每天都要备份mysql数据库,你该如何实现呢,是每天到一定的时间在服务器上敲一遍mysql的备份命令,还是想写个脚本,定时定点的自动备份呢 ...
- phpstrom怎样显示类的方法或函数列表
phpstorm是能显示类的函数或方法列表的. 打开phpstorm,鼠标放到编辑器的右下角(矩形加一个下划线,跟电视机的图标差不多),不用点击就能显示出来一个弹窗: 让后点击Structure,就出 ...
- Lake Counting(dfs)
Description Due to recent rains, water has pooled in various places in Farmer John's field, which is ...
- hadoop核心组件概述及hadoop集群的搭建
什么是hadoop? Hadoop 是 Apache 旗下的一个用 java 语言实现开源软件框架,是一个开发和运行处理大规模数据的软件平台.允许使用简单的编程模型在大量计算机集群上对大型数据集进行分 ...
- CMDB(资产管理系统) day1
运维自动化最重要的就是标准化一切 自动化运维则支持以下功能: 1.OS的选择统一化,同一个项目使用同样的OS系统部署其所需要的各类软件.2.软件安装标准化,例如JAVA虚拟机,php,nginx,my ...
- 关于debug
2019-04-05 11:18:15 1. debug 需巧用两个工具 1.1 用‘#’把感觉会出错的代码段注释掉 多行注释有两种快捷操作: 在需要注释的多行代码块前后加一组三引号''' 选中代 ...
- Linux命令之---cp/scp
命令简介 cp命令用来复制文件或者目录,是Linux系统中最常用的命令之一.一般情况下,shell会设置一个别名,在命令行下复制文件时,如果目标文件已经存在,就会询问是否覆盖,不管你是否使用-i参数. ...