题面

其实题目不算很难,但是我调试的时候被玄学了,for循环里不写空格会RE,写了才能过。神**调了一个多小时是这么个不知道是什么的玩意(真事,可以问i207M=。=),心态爆炸

发现我们只要找AA或者BB就行了,因为另一半反过来再做一次然后拼起来就可以了,那么就设$stp[i]$表示从$i$开始有多少个$AA$这样的串,$edp[i]$表示在$i$结束有多少个$AA$这样的串。一个个位置暴力求是$O(n^2)$的,可以得95pts(雾。

AC做法是一种巧妙(套路?毕竟我做题少)的做法。枚举一个len把串分成长度为$len$的段,然后发现形如$AA$的字符串一定至少跨过了两个分界点,那么我们求一下这两个分界点的$LCP$和$LCS$,看看是不是超过$len$即可,然后具体的贡献可以用差分实现,时间复杂度$O(n\log n)$(不知道为啥$n$只出了30000,可能是为了放哈希+二分的$O(n\log^2 n)$过去?)。

 #include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,K=;
struct a
{
char str[N];
int sec[N],bkt[N];
int sar[N],rnk[N],hgt[N],st[N][K];
int len,siz;
void Set()
{
len=,siz=;
memset(sec,,sizeof sec);
memset(rnk,,sizeof rnk);
}
void Prework()
{
register int i;
for(i=;i<=len;i++)
rnk[i]=str[i]-'a'+,sec[i]=i;
}
void Basenum_Sort()
{
register int i;
for(i=;i<=siz;++i) bkt[i]=;
for(i=;i<=len;++i) ++bkt[rnk[i]];
for(i=;i<=siz;++i) bkt[i]+=bkt[i-];
for(i=len;i>=;--i) sar[bkt[rnk[sec[i]]]--]=sec[i];
}
void Suffix_Sort()
{
register int i;
int cnt=,pw=;
Basenum_Sort();
while(cnt<len)
{
cnt=;
for(i=;i<=pw;i++) sec[++cnt]=len-pw+i;
for(i=;i<=len;i++) if(sar[i]>pw) sec[++cnt]=sar[i]-pw;
Basenum_Sort(); swap(rnk,sec); rnk[sar[]]=cnt=;
for(i=;i<=len;i++)
cnt+=(sec[sar[i-]]!=sec[sar[i]]||sec[sar[i-]+pw]!=sec[sar[i]+pw]),rnk[sar[i]]=cnt;
pw<<=,siz=cnt;
}
}
void Getting_Height()
{
register int i,p=;
for(i=;i<=len;i++)
if(rnk[i]!=)
{
int r=sar[rnk[i]-];
while(str[r+p]==str[i+p]) p++;
hgt[rnk[i]]=p; if(p>) p--;
}
hgt[]=;
}
void Building_Table()
{
register int i,j;
for(i=;i<=len;i++)
st[i][]=hgt[i];
int lgg=log2(len);
for(i=;i<=lgg;i++)
for(j=;j<=len-(<<i)+;j++)
st[j][i]=min(st[j][i-],st[j+(<<(i-))][i-]);
}
int LCP_Query(int x,int y)
{
int xx=rnk[x],yy=rnk[y],lgg;
if(xx>yy) swap(xx,yy); xx++,lgg=log2(yy-xx+);
return min(st[xx][lgg],st[yy-(<<lgg)+][lgg]);
}
}SA[];
int n,lth,stp[N],edp[N];
void Init()
{
SA[].Set(),SA[].Set();
memset(stp,,sizeof stp);
memset(edp,,sizeof edp);
}
int main()
{
register int i,j,k,h;
scanf("%d",&n);
for(i=;i<=n;i++)
{
Init(); scanf("%s",SA[].str+);
SA[].len=SA[].len=lth=strlen(SA[].str+);
for(j=;j<=lth;j++)
SA[].str[j]=SA[].str[lth-j+];
for(j=;j<=;j++)
{
SA[j].Prework();
SA[j].Suffix_Sort();
SA[j].Getting_Height();
SA[j].Building_Table();
}
for(j=;j<=lth/;j++)
{
for(k=j,h=*j;h<=lth;k+=j,h+=j)
{
int l1=min(SA[].LCP_Query(k,h),j);
int l2=min(SA[].LCP_Query(lth-k+,lth-h+),j);
if(l1+l2>j)
{
stp[k-l2+]++,edp[h-l2+j]++;
stp[k+l1-j+]--,edp[h+l1]--;
}
}
}
long long ans=;
for(j=;j<=lth;j++)
stp[j]+=stp[j-],edp[j]+=edp[j-];
for(j=;j<lth;j++)
ans+=1ll*stp[j+]*edp[j];
printf("%lld\n",ans);
}
return ;
}

Upd on 2019.3.16:用SAM搞过去了,然而因为常数原因被同样复杂度的SA踩了

你问怎么做到同样复杂度?写个RMQ LCA就行了(我就因为写这个才学的RMQ LCA=。=)

 #include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,K=;
struct SAM
{
char str[N];
int p[N],noww[N],goal[N];
int dfn[N],idf[N],fir[*N],st[N][K];
int trs[N][],fth[N],len[N],ndp[N];
int lth,lst,tot,cnt,dfo,app;
void Init()
{
cnt=dfo=app=,tot=lst=;
memset(p,,sizeof p);
memset(fth,,sizeof fth);
memset(len,,sizeof len);
memset(trs,,sizeof trs);
}
void Link(int f,int t)
{
noww[++cnt]=p[f];
goal[cnt]=t,p[f]=cnt;
}
int Insert(int ch)
{
int nde=lst,newn=++tot;
lst=newn,len[newn]=len[nde]+;
while(nde&&!trs[nde][ch])
trs[nde][ch]=newn,nde=fth[nde];
if(!nde) fth[newn]=;
else
{
int tran=trs[nde][ch];
if(len[tran]==len[nde]+)
fth[newn]=tran;
else
{
int rnde=++tot; len[rnde]=len[nde]+;
for(int i=;i<=;i++) trs[rnde][i]=trs[tran][i];
fth[rnde]=fth[tran],fth[tran]=fth[newn]=rnde;
while(nde&&trs[nde][ch]==tran)
trs[nde][ch]=rnde,nde=fth[nde];
}
}
return newn;
}
void DFS(int nde)
{
idf[dfn[nde]=++dfo]=nde;
st[fir[nde]=++app][]=dfo;
for(int i=p[nde];i;i=noww[i])
DFS(goal[i]),st[++app][]=dfn[nde];
}
int LCA(int x,int y)
{
x=fir[x],y=fir[y];
if(x>y) swap(x,y);
int l2=log2(y-x+);
return idf[min(st[x][l2],st[y-(<<l2)+][l2])];
}
void Create()
{
for(int i=;i<=lth;i++)
ndp[i]=Insert(str[i]-'a');
for(int i=;i<=tot;i++)
Link(fth[i],i); DFS();
for(int i=;i<=;i++)
for(int j=;j+(<<i)-<=app;j++)
st[j][i]=min(st[j][i-],st[j+(<<(i-))][i-]);
}
int LCS(int x,int y)
{
int lca=LCA(ndp[x],ndp[y]);
return len[lca];
}
}s[];
int n,m,stp[N],edp[N];
void Init()
{
memset(stp,,sizeof stp);
memset(edp,,sizeof edp);
}
int main()
{
register int i,j,k,h;
scanf("%d",&n);
for(i=;i<=n;i++)
{
Init(); scanf("%s",s[].str+);
s[].lth=s[].lth=m=strlen(s[].str+);
for(j=;j<=m;j++) s[].str[j]=s[].str[m-j+];
s[].Init(),s[].Create();
s[].Init(),s[].Create();
for(j=;j<=(m>>);j++)
{
for(k=j,h=*j;h<=m;k+=j,h+=j)
{
int l1=min(s[].LCS(k,h),j);
int l2=min(s[].LCS(m-k+,m-h+),j);
if(l1+l2>j)
{
stp[k-l1+]++,edp[h-l1+j]++;
stp[k+l2-j+]--,edp[h+l2]--;
}
}
}
long long ans=;
for(j=;j<=m;j++) stp[j]+=stp[j-],edp[j]+=edp[j-];
for(j=;j<m;j++) ans+=1ll*stp[j+]*edp[j];
printf("%lld\n",ans);
}
return ;
}

解题:NOI 2016 优秀的拆分的更多相关文章

  1. [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分

    [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...

  2. NOI 2016 优秀的拆分 (后缀数组+差分)

    题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...

  3. 字符串(后缀自动机):NOI 2016 优秀的拆分

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

  4. [NOI 2016]优秀的拆分

    Description 题库链接 给你一个长度为 \(n\) 的只含小写字母的字符串 \(S\) ,计算其子串有多少优秀的拆分. 如果一个字符串能被表示成 \(AABB\) 的形式,其中 \(A,B\ ...

  5. [bzoj 4650][NOI 2016]优秀的拆分

    传送门 Description 如果一个字符串可以被拆分为\(AABB\) 的形式,其中$ A$和 \(B\)是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aabaaba ...

  6. 【NOI 2016】优秀的拆分

    Problem Description 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 \(A\) 和 \(B\) 是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 ...

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

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

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

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

  9. [BZOJ]4650 优秀的拆分(Noi2016)

    比较有意思的一道后缀数组题.(小C最近是和后缀数组淦上了?) 放在NOI的考场上.O(n^3)暴力80分,O(n^2)暴力95分…… 即使想把它作为一道签到题也不要这么随便啊摔(╯‵□′)╯︵┻━┻ ...

随机推荐

  1. 深度学习之神经网络核心原理与算法-caffe&keras框架图片分类

    之前我们在使用cnn做图片分类的时候使用了CIFAR-10数据集 其他框架对于CIFAR-10的图片分类是怎么做的 来与TensorFlow做对比. Caffe Keras 安装 官方安装文档: ht ...

  2. LeetCode 657. Robot Return to Origin (C++)

    题目: There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its ...

  3. TFS任务预览

    不太熟悉TFS任务项的建立. 初步建立及按老师要求分配到个人的任务设置与时间安排如下: (长时间任务可由多人合作完成,具体根据情况迅速调整任务分配) 加上每人需要进行阅读前一小组的代码需要时间2*8= ...

  4. iOS开发学习-NSUserDefaults的介绍和用法

    NSUserDefaults类提供了一个与默认系统进行交互的编程接口.NSUserDefaults对象是用来保存,恢复应用程序相关的偏好设置,配置数据等等.默认系统允许应用程序自定义它的行为去迎合用户 ...

  5. srTCM和trTCM介绍

    本文是用于QoS Meter功能的算法的RFC的阅读笔记.DPDK的QoS_meter示例程序用的就是这个算法. srTCM srTCM的英文全称是Single Rate Three Color Ma ...

  6. vue-cli 安装步骤(转载)

    参考资料:Vue2.0 新手完全填坑攻略—从环境搭建到发布 1.Node.js安装 https://nodejs.org/en/download/ 2.安装vue-cli npm install -g ...

  7. qq飞车精灵家园里的背景音乐:Mysterious Town pooka 下载

      一直都觉得Mysterious Town pooka特别好听,但是酷狗音乐和网上直接搜搜不到,于是我直接从源文件中找了出来.虽然是.ogg格式,但是在酷狗音乐里还是可以播放的.貌似是<奥丁领 ...

  8. PAT 1034 有理数四则运算

    https://pintia.cn/problem-sets/994805260223102976/problems/994805287624491008 本题要求编写程序,计算2个有理数的和.差.积 ...

  9. [转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?     http://www.52im.net/thread-1732-1-1.html   1.引言 本文接上篇<脑残式网 ...

  10. QT源码解析(七)Qt创建窗体的过程,作者“ tingsking18 ”(真正的创建QPushButton是在show()方法中,show()方法又调用了setVisible方法)

    前言:分析Qt的代码也有一段时间了,以前在进行QT源码解析的时候总是使用ue,一个函数名在QTDIR/src目录下反复的查找,然后分析函数之间的调用关系,效率实在是太低了,最近总结出一个更简便的方法, ...