解题: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分…… 即使想把它作为一道签到题也不要这么随便啊摔(╯‵□′)╯︵┻━┻ ...
随机推荐
- 03_set slice的时间复杂度
set slice O(n+k) 使用切片赋值来解释set slice的时间复杂度 (1) 对li[0:3]赋值首先会删除1,2,3,空出来的位置被后面的元素依次向前移动填充,由del slice 得 ...
- JDK8 metaspace调优
从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间.Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大 ...
- 第一个scrim任务分布
一.项目经理:郭健豪 二.scrim分工 杨广鑫.郭健豪:制作第一个精选页面布局,和代码实现.如:实现图书推荐布局中图书的排布,搜索框代码的实现,消息提示的跳转 李明.郑涛:实现第一个精选页面数据库的 ...
- VANET
VANET知识 VANET与普通网络相比,与IOV的区别: VANET中Greedy Routing:基于距离(GPSR):基于速度和角度:基于道路层(TDR): Repair Strategy:Fa ...
- 【Coursera】主成分分析
一.问题 主方向的概念是什么?为什么降低维度的方法是使方差最大化? 假设某两个特征之间成线性关系,在二维平面上的表示就是数据点呈线性分布,那么可以通过将数据在主方向上进行投影,得到一个一维的数据,这个 ...
- 《TCP/IP 详解 卷1:协议》第 4 章:地址解析协议
链路层是经过单一链路通信的协议层. IP 网络层协议的设计目标是为跨越不同物理类型的.多节点网络的 packet ,提供主机寻址.路由操作. 在其中要注意的一点是:网络层使用的地址和底层网络硬件使用的 ...
- Alpha阶段敏捷冲刺①Scrum 冲刺博客
第 1 篇 Scrum 冲刺博客对整个冲刺阶段起到领航作用,应该主要包含三个部分的内容: 各个成员在 Alpha 阶段认领的任务 成员 任务 张晨晨 完成界面设计(前端) 黄登峰 完成界面设计(前端) ...
- 【第七周】B-1分数发布
组名: 新蜂 组长: 武志远 组员: 宫成荣 谢孝淼 杨柳 李峤 项目名称: java俄罗斯方块 由于排名信息过于敏感,由以下方式进行. 宫成荣 = 魑,谢孝淼 = 魅,武志远 = 魉,杨柳 = ...
- scrapy 直接在编辑器运行
# *_*coding:utf-8 *_* from scrapy.cmdline import execute execute("scrapy crawl spbeen --nolog&q ...
- php $_SERVER['HTTP_USER_AGENT']
//获取浏览器 function getBrowse() { global $_SERVER; $Agent = $_SERVER['HTTP_USER_AGENT']; $browseinfo='' ...