解题: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分…… 即使想把它作为一道签到题也不要这么随便啊摔(╯‵□′)╯︵┻━┻ ...
随机推荐
- Mac 终端快捷键
ctrl+A 跳转到行开头 ctrl+E 跳转到行结尾 ctrl+U 清空当前行 Command+K 清屏 Command+→多终端页面跳转 ...
- Node.js文档和教程
七天学会NodeJS:https://nqdeng.github.io/7-days-nodejs/ Node入门:http://www.nodebeginner.org/index-zh-cn.ht ...
- C++ Style Languages: C++, Objective-C, Java, C#
Hyperpolyglot.org From Hyperpolyglot.org C++ Style Languages: C++, Objective-C, Java, C# a side-by-s ...
- PyCharm配置SFTP远程调试Django应用
http://www.ithao123.cn/content-41747.html http://www.th7.cn/system/lin/201703/205998.shtml
- 使用谷歌浏览器调试WEB前端的一些必备调试技巧
转载:http://www.techug.com/post/chrome-debug-tips.html Chrome的开发者工具是个很强大的东西,相信程序员们都不会陌生,不过有些小功能可能并不为大众 ...
- oracle和mysql对时间与字符串的转换
1,oracle to_date(#{item.value},'YYYY-MM-DD hh24-mi-ss') to_char(CRERATE_TIME,'YYYY-MM-DD hh24-mi-ss' ...
- Linux基础入门--01~03
- 软工实践-Beta 冲刺 (4/7)
队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记 ...
- “吃神么,买神么”的第二个Sprint计划(计划过程内容)
“吃神么,买神么”项目Sprint计划 ——6.1(第二天)立会内容与进度 团队组员各自任务: 陈键.吴舒婷:继续完善前台设局与布局 林欢雯.冯美欣:开展后台的界面的设计与布局 任务的进度: 陈键. ...
- DPDK QoS_meter 源码阅读
main.c /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2010-2016 Intel Corporation */ #inclu ...