$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」优秀的拆分的更多相关文章

  1. 「NOI2016」优秀的拆分 解题报告

    「NOI2016」优秀的拆分 这不是个SAM题,只是个LCP题目 95分的Hash很简单,枚举每个点为开头和末尾的AA串个数,然后乘一下之类的. 考虑怎么快速求"每个点为开头和末尾的AA串个 ...

  2. loj#2128. 「HAOI2015」数字串拆分 矩阵乘法

    目录 题目链接 题解 代码 题目链接 loj#2128. 「HAOI2015」数字串拆分 题解 \(f(s)\)对于\(f(i) = \sum_{j = i - m}^{i - 1}f(j)\) 这个 ...

  3. LOJ#2086. 「NOI2016」区间

    $n \leq 500000$个区间,从中挑出一些,使得至少有一个点被$m$个选中区间包含,且选中区间长度的极差最小. 区间题死脑筋晚期:把区间按左端点排序,然后右端点用个优先队列来弹,然后需要维护下 ...

  4. *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 ...

  5. LOJ#2084. 「NOI2016」网格

    $n,m \leq 1e9$,$n*m$的网格中有$c \leq 1e5$个是黑的,其他是白的.问:使至少两个白的不连通,最少需要再把几个白的涂黑. 可以发现答案是-1,0,1,2啦.-1要么没白的, ...

  6. Loj #2554. 「CTSC2018」青蕈领主

    Loj #2554. 「CTSC2018」青蕈领主 题目描述 "也许,我的生命也已经如同风中残烛了吧."小绿如是说. 小绿同学因为微积分这门课,对"连续"这一概 ...

  7. Loj #2570. 「ZJOI2017」线段树

    Loj #2570. 「ZJOI2017」线段树 题目描述 线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构.优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质. 最近可怜 ...

  8. UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]

    #219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...

  9. 【BZOJ4650】【NOI2016】优秀的拆分(后缀数组)

    [BZOJ4650][NOI2016]优秀的拆分(后缀数组) 题面 BZOJ Uoj 题解 如果我们知道以某个位置为开始/结尾的\(AA\)串的个数 那就直接做一下乘法就好 这个怎么求? 枚举一个位置 ...

随机推荐

  1. 3170: [Tjoi2013]松鼠聚会

    Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1804  Solved: 968[Submit][Status][Discuss] Descript ...

  2. 1074: [SCOI2007]折纸origami

    Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 372  Solved: 229[Submit][Status][Discuss] Descriptio ...

  3. IATHook

    IATHookClass.h #pragma once #include <Windows.h> class IATHookClass { private: DWORD oldAddr; ...

  4. Vue入门之HelloWorld

    对于新学习一门技术,一门语言比如JAVA.Python等都是从编写Hello World开始的,这样一来有益于初学者的人门,并给予初学者一定的信心,所以我也从HelloWorld开始讲起. 干货: 对 ...

  5. Java poi 导出Excel并下载到客户端

    Maven配置,包含了其他文件格式的依赖,就全贴出来了 <dependency> <groupId>org.apache.poi</groupId> <art ...

  6. JZOJ 5849 d

    Description Input Output Data Constraint 做法:考虑贪心使得mina*minb最大,先以a为关键字排序,然后枚举删除最小的0~m个ai,对应删除最小的bi,然后 ...

  7. manjaro中文输入法已安装但切换不了解决方法

    情况如图所示,输入法安装了,但Ctrl+空格键或者鼠标选择切换都不行 解决方法: 打开家目录下面的.xprofile文件,如果没有这个文件就新建一个,加入下面内容 保存文件,退出. 重启电脑就可以了

  8. A * B Problem Plus HDU - 1402 (FFT)

    A * B Problem Plus HDU - 1402 (FFT) Calculate A * B.  InputEach line will contain two integers A and ...

  9. hadoop: Shuffle过程详解 (转载)

    原文地址:http://langyu.iteye.com/blog/992916 另一篇博文:http://www.cnblogs.com/gwgyk/p/3997849.html Shuffle过程 ...

  10. 命令java 找不到或无法加载主类

    这个是由于用了package导致cmd下找不到class文件产生的错误,解决方案: 方法1.删除HelloWord.java源程序中的第一行package demo1:然后在cmd下正常使用javac ...