题面

BZOJ
Uoj

题解

如果我们知道以某个位置为开始/结尾的AAAA串的个数
那就直接做一下乘法就好
这个怎么求?
枚举一个位置
枚举串的长度
直接暴力算就好啦
至于是否可行,用SASA求lcplcp就好啦
这样就是9595分
NOI这么好拿部分分的???

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 35000
int x[MAX],y[MAX],t[MAX];
int SA[MAX],height[MAX],rk[MAX];
int lg[MAX],n,p[20][MAX],a[MAX];
char s[MAX];
int g[MAX],f[MAX],T;
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void init()
{memset(SA,0,sizeof(SA));memset(height,0,sizeof(height));memset(rk,0,sizeof(rk));memset(x,0,sizeof(x));memset(y,0,sizeof(y));memset(t,0,sizeof(t));memset(a,0,sizeof(a));
}
void GetSA()
{int m=50;for(int i=1;i<=n;++i)t[x[i]=a[i]]++;for(int i=1;i<=m;++i)t[i]+=t[i-1];for(int i=n;i>=1;--i)SA[t[x[i]]--]=i;for(int k=1;k<=n;k<<=1){int p=0;for(int i=n-k+1;i<=n;++i)y[++p]=i;for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;for(int i=0;i<=m;++i)t[i]=0;for(int i=1;i<=n;++i)t[x[y[i]]]++;for(int i=1;i<=m;++i)t[i]+=t[i-1];for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];swap(x,y);x[SA[1]]=p=1;for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;if(p>=n)break;m=p;}for(int i=1;i<=n;++i)rk[SA[i]]=i;for(int i=1,j=0;i<=n;++i){if(j)--j;while(a[i+j]==a[SA[rk[i]-1]+j])++j;height[rk[i]]=j;}
}
void Pre()
{memset(p,63,sizeof(p));for(int i=1;i<=n;++i)p[0][i]=height[i];for(int j=1;j<15;++j)for(int i=1;i<=n;++i)p[j][i]=min(p[j-1][i],p[j-1][i+(1<<(j-1))]);
}
int Query(int i,int j)
{return min(p[lg[j-i+1]][i],p[lg[j-i+1]][j-(1<<lg[j-i+1])+1]);
}
int lcp(int i,int j)
{int l=min(rk[i],rk[j])+1,r=max(rk[i],rk[j]);return Query(l,r);
}
int main()
{for(int i=2;i<=30000;++i)lg[i]=lg[i>>1]+1;scanf("%d",&T);while(T--){init();scanf("%s",s+1);n=strlen(s+1);for(int i=1;i<=n;++i)a[i]=s[i]-96;GetSA();Pre();for(int i=1;i<=n;++i){g[i]=0;for(int l=1;l+l+i-1<=n;++l)if(lcp(i,i+l)>=l)g[i]++;}for(int i=2;i<=n;++i){f[i]=0;for(int l=1;i-l-l+1>0;++l)if(lcp(i-l-l+1,i-l+1)>=l)f[i]++;}int ans=0;for(int i=1;i<n;++i)ans+=f[i]*g[i+1];printf("%d\n",ans);}return 0;
}

9595分的暴力太显然了。。
原来NOINOI都是这样送分???
为什么NOIP 没有这么好的福利


想想怎么优化吧。。。
肯定不能枚举长度之后再暴力算每一个位置
那么,我们要考虑一个方法,
可以一次性算出连续的位置

想想我们怎么求AAAA这种形式??
计算lcp(i,i+len)>=lenlcp(i,i+len)>=len是否成立
但是,如果lcp(i,i+len)>=lenlcp(i,i+len)>=len
我们就会发现,有一段区间内都是有满足条件的子串
所以我们可以一起计算

现在仔细思考怎么算
因为每次是ii和i+leni+len
所以我们只要枚举位置是lenlen的倍数的地方就好
旁边的地方我们要想办法算出来
第一个,是向后如果可以增加的话
lcp(i,i+len)>=Llcp(i,i+len)>=L我就会获得向后的一段连续区间
如果只算向后,会忽略掉向前的一段
所以再算一下lcs(i,i+len)lcs(i,i+len)这段,这两边拼起来
如果满足条件,证明这一段区间都是可行的
这样就可以差分全部+1+1
如果重复的部分够多
这样算可能会影响到别的块里面
所以要强制只在自己这一段里面算
具体的实现看代码啦

 #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 35000
int lg[MAX],n;
char s[MAX];
int g[MAX],f[MAX],T;
struct SA
{int p[20][MAX],a[MAX];int x[MAX],y[MAX],t[MAX];int SA[MAX],height[MAX],rk[MAX];bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}void init(){memset(SA,0,sizeof(SA));memset(height,0,sizeof(height));memset(rk,0,sizeof(rk));memset(x,0,sizeof(x));memset(y,0,sizeof(y));memset(t,0,sizeof(t));memset(a,0,sizeof(a));}void GetSA(){int m=50;for(int i=1;i<=n;++i)t[x[i]=a[i]]++;for(int i=1;i<=m;++i)t[i]+=t[i-1];for(int i=n;i>=1;--i)SA[t[x[i]]--]=i;for(int k=1;k<=n;k<<=1){int p=0;for(int i=n-k+1;i<=n;++i)y[++p]=i;for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;for(int i=0;i<=m;++i)t[i]=0;for(int i=1;i<=n;++i)t[x[y[i]]]++;for(int i=1;i<=m;++i)t[i]+=t[i-1];for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];swap(x,y);x[SA[1]]=p=1;for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;if(p>=n)break;m=p;}for(int i=1;i<=n;++i)rk[SA[i]]=i;for(int i=1,j=0;i<=n;++i){if(j)--j;while(a[i+j]==a[SA[rk[i]-1]+j])++j;height[rk[i]]=j;}}void Pre(){memset(p,63,sizeof(p));for(int i=1;i<=n;++i)p[0][i]=height[i];for(int j=1;j<15;++j)for(int i=1;i<=n;++i)p[j][i]=min(p[j-1][i],p[j-1][i+(1<<(j-1))]);}int Query(int i,int j){return min(p[lg[j-i+1]][i],p[lg[j-i+1]][j-(1<<lg[j-i+1])+1]);}int lcp(int i,int j){int l=min(rk[i],rk[j])+1,r=max(rk[i],rk[j]);return Query(l,r);}
}A,B;
int main()
{for(int i=2;i<=30000;++i)lg[i]=lg[i>>1]+1;scanf("%d",&T);while(T--){A.init();B.init();scanf("%s",s+1);n=strlen(s+1);for(int i=1;i<=n;++i)A.a[i]=s[i]-96;for(int i=1;i<=n;++i)B.a[n-i+1]=s[i]-96;A.GetSA();A.Pre();B.GetSA();B.Pre();for(int i=1;i<=n;++i)g[i]=f[i]=0;for(int len=1;len<=n/2;++len){for(int i=len,j=i+len;j<=n;i+=len,j+=len){int x=min(A.lcp(i,j),len);int y=min(B.lcp(n-i+2,n-j+2),len-1);int t=x+y-len+1;if(x+y>=len){g[i-y]++;g[i-y+t]--;f[j+x-t]++;f[j+x]--;}}}for(int i=1;i<=n;++i)g[i]+=g[i-1];for(int i=1;i<=n;++i)f[i]+=f[i-1];ll ans=0;for(int i=1;i<n;++i)ans+=1ll*f[i]*g[i+1];printf("%lld\n",ans);}return 0;
}

【BZOJ4650】【NOI2016】优秀的拆分(后缀数组)相关推荐

  1. UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组.ST表) 连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题 ...

  2. BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)

    BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...

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

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

  4. [NOI2016] 优秀的拆分 题解

    [NOI2016] 优秀的拆分 题解 link 题意 \(T\) 组询问,每组一个字符串 \(s\) 求 \(s\) 所有字串分成 \(AABB\) 的方案数之和. \(A,B\) 为非空串. 题解 ...

  5. P1117 [NOI2016]优秀的拆分

    $ \color{#0066ff}{ 题目描述 }$ 如果一个字符串可以被拆分为\(AABB\)的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aab ...

  6. NOI2016 优秀的拆分(图解)

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

  7. 【背板子-后缀数组】BZOJ4199 BZOJ4650 LGP5108 CF504E

    [前言] 后缀数组一直是我没有背的板子来着. 强行背了下来. adjust这个函数丢不掉了. [题目] BZOJ4199 [NOI2015] 品酒大会 BZOJ 按heightheightheight ...

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

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

  9. 【NOI2016】优秀的拆分【后缀数组】【ST表】【关键点】【调和级数复杂度】【差分】

    传送门 题意:如果一个字符串可以拆分为AABB的形式,其中A和B是任意非空字符串,则我们这种拆分是优秀的.求给定串的所有子串的拆分方案数之和. N≤30000N \leq30000N≤30000 本来 ...

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

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

最新文章

  1. java round half down_Java8中的RoundingMode.HALF_DOWN问题
  2. python处理流程-Python文件处理流程
  3. C语言:内存的分配与管理
  4. leetcode109. 有序链表转换二叉搜索树(深度优先搜索/快慢指针)
  5. mysql 索引及索引创建原则
  6. 子恩域名授权系统2.0全解无加密无授权版盗版入库源码
  7. windows mysql备份与恢复_windows下Mysql自动备份与恢复说明
  8. C++语言虚函数表实现多态原理
  9. yv12转nv12,nv12转I420
  10. webGL学习笔记一
  11. MySQL主从介绍及配置
  12. Unity之FBX文件操作学习笔记(一)
  13. B端、C端产品的区别|产品经理面试题第1篇|||答题思路
  14. 笔记本插入耳机声音外放
  15. Relatively Prime Graph(贪心+注意数据范围)
  16. 你家的wifi安全么?
  17. STM32 ADC多通道采样声音传感器和环境光传感器
  18. VS错误代码列表之美
  19. 软测—直播教学 黑盒测试
  20. 三角警示牌emark认证详解

热门文章

  1. 2022-2028年中国透气膜复合无纺布行业市场运行格局及战略咨询研究报告
  2. 通用能力-判断推理专项练习(3)
  3. 互联网保险市场规模持续上涨,元保保险发布权威报告公开数据
  4. debounce实现 js_聊聊lodash的debounce实现
  5. ubuntu下pycharm输入中文不正常(出现下划线)的解决方法
  6. ADAS相关的一些汽车术语
  7. 很高兴认识大家。新人请多担待
  8. 我的世界服务器修改末地难度,我的世界带你解锁进入末地的高难度姿势
  9. 第一次使用无人船的日志
  10. JAVA计算机毕业设计学校食堂订餐管理Mybatis+源码+数据库+lw文档+系统+调试部署