解题: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)$过去?)。
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=30005,K=16; 7 struct a 8 { 9 char str[N]; 10 int sec[N],bkt[N]; 11 int sar[N],rnk[N],hgt[N],st[N][K]; 12 int len,siz; 13 void Set() 14 { 15 len=0,siz=30; 16 memset(sec,0,sizeof sec); 17 memset(rnk,0,sizeof rnk); 18 } 19 void Prework() 20 { 21 register int i; 22 for(i=1;i<=len;i++) 23 rnk[i]=str[i]-'a'+1,sec[i]=i; 24 } 25 void Basenum_Sort() 26 { 27 register int i; 28 for(i=1;i<=siz;++i) bkt[i]=0; 29 for(i=1;i<=len;++i) ++bkt[rnk[i]]; 30 for(i=1;i<=siz;++i) bkt[i]+=bkt[i-1]; 31 for(i=len;i>=1;--i) sar[bkt[rnk[sec[i]]]--]=sec[i]; 32 } 33 void Suffix_Sort() 34 { 35 register int i; 36 int cnt=0,pw=1; 37 Basenum_Sort(); 38 while(cnt<len) 39 { 40 cnt=0; 41 for(i=1;i<=pw;i++) sec[++cnt]=len-pw+i; 42 for(i=1;i<=len;i++) if(sar[i]>pw) sec[++cnt]=sar[i]-pw; 43 Basenum_Sort(); swap(rnk,sec); rnk[sar[1]]=cnt=1; 44 for(i=2;i<=len;i++) 45 cnt+=(sec[sar[i-1]]!=sec[sar[i]]||sec[sar[i-1]+pw]!=sec[sar[i]+pw]),rnk[sar[i]]=cnt; 46 pw<<=1,siz=cnt; 47 } 48 } 49 void Getting_Height() 50 { 51 register int i,p=0; 52 for(i=1;i<=len;i++) 53 if(rnk[i]!=1) 54 { 55 int r=sar[rnk[i]-1]; 56 while(str[r+p]==str[i+p]) p++; 57 hgt[rnk[i]]=p; if(p>0) p--; 58 } 59 hgt[1]=0; 60 } 61 void Building_Table() 62 { 63 register int i,j; 64 for(i=1;i<=len;i++) 65 st[i][0]=hgt[i]; 66 int lgg=log2(len); 67 for(i=1;i<=lgg;i++) 68 for(j=1;j<=len-(1<<i)+1;j++) 69 st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]); 70 } 71 int LCP_Query(int x,int y) 72 { 73 int xx=rnk[x],yy=rnk[y],lgg; 74 if(xx>yy) swap(xx,yy); xx++,lgg=log2(yy-xx+1); 75 return min(st[xx][lgg],st[yy-(1<<lgg)+1][lgg]); 76 } 77 }SA[2]; 78 int n,lth,stp[N],edp[N]; 79 void Init() 80 { 81 SA[0].Set(),SA[1].Set(); 82 memset(stp,0,sizeof stp); 83 memset(edp,0,sizeof edp); 84 } 85 int main() 86 { 87 register int i,j,k,h; 88 scanf("%d",&n); 89 for(i=1;i<=n;i++) 90 { 91 Init(); scanf("%s",SA[0].str+1); 92 SA[0].len=SA[1].len=lth=strlen(SA[0].str+1); 93 for(j=1;j<=lth;j++) 94 SA[1].str[j]=SA[0].str[lth-j+1]; 95 for(j=0;j<=1;j++) 96 { 97 SA[j].Prework(); 98 SA[j].Suffix_Sort(); 99 SA[j].Getting_Height(); 100 SA[j].Building_Table(); 101 } 102 for(j=1;j<=lth/2;j++) 103 { 104 for(k=j,h=2*j;h<=lth;k+=j,h+=j) 105 { 106 int l1=min(SA[0].LCP_Query(k,h),j); 107 int l2=min(SA[1].LCP_Query(lth-k+1,lth-h+1),j); 108 if(l1+l2>j) 109 { 110 stp[k-l2+1]++,edp[h-l2+j]++; 111 stp[k+l1-j+1]--,edp[h+l1]--; 112 } 113 } 114 } 115 long long ans=0; 116 for(j=1;j<=lth;j++) 117 stp[j]+=stp[j-1],edp[j]+=edp[j-1]; 118 for(j=1;j<lth;j++) 119 ans+=1ll*stp[j+1]*edp[j]; 120 printf("%lld\n",ans); 121 } 122 return 0; 123 }
View Code
Upd on 2019.3.16:用SAM搞过去了,然而因为常数原因被同样复杂度的SA踩了
你问怎么做到同样复杂度?写个RMQ LCA就行了(我就因为写这个才学的RMQ LCA=。=)
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=60005,K=17; 7 struct SAM 8 { 9 char str[N]; 10 int p[N],noww[N],goal[N]; 11 int dfn[N],idf[N],fir[2*N],st[N][K]; 12 int trs[N][26],fth[N],len[N],ndp[N]; 13 int lth,lst,tot,cnt,dfo,app; 14 void Init() 15 { 16 cnt=dfo=app=0,tot=lst=1; 17 memset(p,0,sizeof p); 18 memset(fth,0,sizeof fth); 19 memset(len,0,sizeof len); 20 memset(trs,0,sizeof trs); 21 } 22 void Link(int f,int t) 23 { 24 noww[++cnt]=p[f]; 25 goal[cnt]=t,p[f]=cnt; 26 } 27 int Insert(int ch) 28 { 29 int nde=lst,newn=++tot; 30 lst=newn,len[newn]=len[nde]+1; 31 while(nde&&!trs[nde][ch]) 32 trs[nde][ch]=newn,nde=fth[nde]; 33 if(!nde) fth[newn]=1; 34 else 35 { 36 int tran=trs[nde][ch]; 37 if(len[tran]==len[nde]+1) 38 fth[newn]=tran; 39 else 40 { 41 int rnde=++tot; len[rnde]=len[nde]+1; 42 for(int i=0;i<=25;i++) trs[rnde][i]=trs[tran][i]; 43 fth[rnde]=fth[tran],fth[tran]=fth[newn]=rnde; 44 while(nde&&trs[nde][ch]==tran) 45 trs[nde][ch]=rnde,nde=fth[nde]; 46 } 47 } 48 return newn; 49 } 50 void DFS(int nde) 51 { 52 idf[dfn[nde]=++dfo]=nde; 53 st[fir[nde]=++app][0]=dfo; 54 for(int i=p[nde];i;i=noww[i]) 55 DFS(goal[i]),st[++app][0]=dfn[nde]; 56 } 57 int LCA(int x,int y) 58 { 59 x=fir[x],y=fir[y]; 60 if(x>y) swap(x,y); 61 int l2=log2(y-x+1); 62 return idf[min(st[x][l2],st[y-(1<<l2)+1][l2])]; 63 } 64 void Create() 65 { 66 for(int i=1;i<=lth;i++) 67 ndp[i]=Insert(str[i]-'a'); 68 for(int i=1;i<=tot;i++) 69 Link(fth[i],i); DFS(1); 70 for(int i=1;i<=16;i++) 71 for(int j=1;j+(1<<i)-1<=app;j++) 72 st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]); 73 } 74 int LCS(int x,int y) 75 { 76 int lca=LCA(ndp[x],ndp[y]); 77 return len[lca]; 78 } 79 }s[2]; 80 int n,m,stp[N],edp[N]; 81 void Init() 82 { 83 memset(stp,0,sizeof stp); 84 memset(edp,0,sizeof edp); 85 } 86 int main() 87 { 88 register int i,j,k,h; 89 scanf("%d",&n); 90 for(i=1;i<=n;i++) 91 { 92 Init(); scanf("%s",s[0].str+1); 93 s[0].lth=s[1].lth=m=strlen(s[0].str+1); 94 for(j=1;j<=m;j++) s[1].str[j]=s[0].str[m-j+1]; 95 s[0].Init(),s[0].Create(); 96 s[1].Init(),s[1].Create(); 97 for(j=1;j<=(m>>1);j++) 98 { 99 for(k=j,h=2*j;h<=m;k+=j,h+=j) 100 { 101 int l1=min(s[0].LCS(k,h),j); 102 int l2=min(s[1].LCS(m-k+1,m-h+1),j); 103 if(l1+l2>j) 104 { 105 stp[k-l1+1]++,edp[h-l1+j]++; 106 stp[k+l2-j+1]--,edp[h+l2]--; 107 } 108 } 109 } 110 long long ans=0; 111 for(j=1;j<=m;j++) stp[j]+=stp[j-1],edp[j]+=edp[j-1]; 112 for(j=1;j<m;j++) ans+=1ll*stp[j+1]*edp[j]; 113 printf("%lld\n",ans); 114 } 115 return 0; 116 }
View Code
转载于:https://www.cnblogs.com/ydnhaha/p/10148244.html
解题:NOI 2016 优秀的拆分相关推荐
- NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...
- UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)
UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组.ST表) 连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题 ...
- 信息学奥赛一本通 2004:【20CSPJ普及组】优秀的拆分 | 洛谷 P7071 [CSP-J2020] 优秀的拆分
[题目链接] ybt 2004:[20CSPJ普及组]优秀的拆分 洛谷 P7071 [CSP-J2020] 优秀的拆分 [题目考点] 数制 基数:即进制数.十进制的基数是10,二进制的基数是2. 按位 ...
- [BZOJ]4650 优秀的拆分(Noi2016)
比较有意思的一道后缀数组题.(小C最近是和后缀数组淦上了?) 放在NOI的考场上.O(n^3)暴力80分,O(n^2)暴力95分-- 即使想把它作为一道签到题也不要这么随便啊摔(╯‵□′)╯︵┻━┻ ...
- 基础算法4 —— 结构体(成绩统计) + 栈(模拟进制转换 + 优秀的拆分) + 指针
结构体 结构体的实际应用场景: 在实际问题中,一组数据往往具有不同的数据类型.比如,某次期末考试中要记录一个学生的考试信息,除了有姓名(char)外,还有班级(int).性别(char).语文.数学. ...
- 【NOI2016】优秀的拆分(后缀数组)
题目描述 如果一个字符串可以被拆分为AABBAABB的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串aabaabaa,如果令 A=aab,B=a,我们就找 ...
- CSP - J 2020 T1 优秀的拆分
https://www.luogu.com.cn/problem/P7071 /* CSP - J 2020 T1 优秀的拆分 https://www.luogu.com.cn/problem/P70 ...
- P1117 [NOI2016]优秀的拆分
$ \color{#0066ff}{ 题目描述 }$ 如果一个字符串可以被拆分为\(AABB\)的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aab ...
- [NOI2016] 优秀的拆分 题解
[NOI2016] 优秀的拆分 题解 link 题意 \(T\) 组询问,每组一个字符串 \(s\) 求 \(s\) 所有字串分成 \(AABB\) 的方案数之和. \(A,B\) 为非空串. 题解 ...
- NOI2016 优秀的拆分(图解)
如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符 ...
最新文章
- java定时关机源码_java实现电脑定时关机的方法
- 产品经理如何评估产品机会
- linux 报错 E: 无法定位软件包 python-lzma
- stateflow中终止节点小记
- glid加载不出来图片
- 2018年澳门就业情况理想 最新失业率维持1.7%
- [MySQL FAQ]系列 -- 如何直接覆盖 MYI MYD 文件
- 学习 Kotlin 的 20 个实用资源
- 小米小爱音箱Pro8安装app_小米小爱音箱HD:声与色的美
- SQL语句批量替换某个指定的字符串
- SI4463配置软件wds3
- 代码没问题但运行不出来
- ArcGIS 10安装方法(对比流行的2种安装方法)||迅雷电驴下载地址
- 计算机工程中级职称怎么考,以前中级职称是要考什么计算机-计算机软考中级职称哪个好考...
- cyberduck 源代码学习记录一,编译源代码 build for window
- vscode 插件推荐 - 献给所有前端工程师
- 种类并查集(POJ1703)
- 苹果选了天猫 天猫成了618
- 古文字识别助手与众包平台——项目博客二
- IT增值服务客户案例(二):河南郑州大四实习生,职业规划和项目开发指导