解题:NOI 2016 优秀的拆分
其实题目不算很难,但是我调试的时候被玄学了,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 优秀的拆分的更多相关文章
- [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...
- NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...
- 字符串(后缀自动机):NOI 2016 优秀的拆分
[问题描述] 如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串, 则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A = aab, B ...
- [NOI 2016]优秀的拆分
Description 题库链接 给你一个长度为 \(n\) 的只含小写字母的字符串 \(S\) ,计算其子串有多少优秀的拆分. 如果一个字符串能被表示成 \(AABB\) 的形式,其中 \(A,B\ ...
- [bzoj 4650][NOI 2016]优秀的拆分
传送门 Description 如果一个字符串可以被拆分为\(AABB\) 的形式,其中$ A$和 \(B\)是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aabaaba ...
- 【NOI 2016】优秀的拆分
Problem Description 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 \(A\) 和 \(B\) 是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 ...
- 「NOI2016」优秀的拆分 解题报告
「NOI2016」优秀的拆分 这不是个SAM题,只是个LCP题目 95分的Hash很简单,枚举每个点为开头和末尾的AA串个数,然后乘一下之类的. 考虑怎么快速求"每个点为开头和末尾的AA串个 ...
- 【BZOJ4650】【NOI2016】优秀的拆分(后缀数组)
[BZOJ4650][NOI2016]优秀的拆分(后缀数组) 题面 BZOJ Uoj 题解 如果我们知道以某个位置为开始/结尾的\(AA\)串的个数 那就直接做一下乘法就好 这个怎么求? 枚举一个位置 ...
- [BZOJ]4650 优秀的拆分(Noi2016)
比较有意思的一道后缀数组题.(小C最近是和后缀数组淦上了?) 放在NOI的考场上.O(n^3)暴力80分,O(n^2)暴力95分…… 即使想把它作为一道签到题也不要这么随便啊摔(╯‵□′)╯︵┻━┻ ...
随机推荐
- http跳转https方法:百度云如何让http自动跳转到https【免费SSL证书使用FAQ】
之前的一篇文章已经给大家提供了免费SSL证书的申请方法,这一篇文章是告诉大家在使用免费的SSL证书时可能会遇到的问题[怎么让http自动跳转到https以及http与https同时使用]的解决方法. ...
- Hands on Machine Learning with Sklearn and TensorFlow学习笔记——机器学习概览
一.什么是机器学习? 计算机程序利用经验E(训练数据)学习任务T(要做什么,即目标),性能是P(性能指标),如果针对任务T的性能P随着经验E不断增长,成为机器学习.[这是汤姆米切尔在1997年定义] ...
- 从一个简单的寻路问题深入Q-learning
这第一篇随笔实际上在我的科学网博客上是首发,我重新拿到博客园再发一次是希望以此作为我学习Q-learning的一个新的开始.以后这边主技术,科学网博客主理论.我也会将科学网那边技术类的文章转过来的.希 ...
- Aspose.words Java基于模板生成word之循环图片
1.新建一个word文档 2.给插入图片的地方设置书签 3,设置书签 二,项目 1,2步的引入依赖以及加载授权文件同上一篇 3,获取图片路径插入到word中并生成新的word文档 新文档中,每行显示两 ...
- Microsoft Visual Studio 2013 的安装及单元测试
题目:练习教科书第22~25页单元测试练习,要求自行安装Visual Studio开发平台,版本至少在2010以上,要求把程序安装过程和练习过程写到博客上,越详细越好,要图文并茂. 安装过程: 1.下 ...
- ASP.NET MVC5 学习系列之初探MVC
一.由问题看本质 (一)什么是MVC? MVC是Model-View-Controller的简称.它是在1970年引入的软件设计模式.MVC 模式强迫关注分离 — 域模型和控制器逻辑与UI是松耦合关系 ...
- <构建之法>阅读感想
在阅读<构建之法>之前,我所认为的软件就是通过c,c++等语言编程,制作出的一个能满足人们操作需求的一些代码,认为一个好的软件工程师,就是能够在很短的时间之内,最快的根据需求写出几段代码程 ...
- ListViewAnimations使用时报错NoClassDefFoundError: com.nineoldandroids.animation.Animator
见 https://github.com/nhaarman/ListViewAnimations/issues/294 解决: Add this to your dependencies in you ...
- Codeforces Round #157 (Div. 1) B. Little Elephant and Elections 数位dp+搜索
题目链接: http://codeforces.com/problemset/problem/258/B B. Little Elephant and Elections time limit per ...
- UDP与TCP笔记
1.UDP UDP协议在工作时是建立在IP协议之上的,UDP从进程的缓冲区接收进程每一次产生的输出,对每次输出都生成一个UDP数据报,然后把生成的UDP数据报直接封装在IP数据报中进行传输,因此在传输 ...