「雅礼集训 2017 Day7」事情的相似度

题目链接

我们先将字符串建后缀自动机。然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点的\(lca\)的\(maxlen\)。

所以现在问题就变成了一个树上问题:给定一棵树,每个点有一个权值\((mxlen)\),询问编号在一段区间内的点两两之间\(lca\)权值的最大值。

方法很多,这里用的\(dsu\ on\ tree\)。对于每个点\(v\),我们计算其作为\(lca\)的贡献。显然贡献的情况是一个点对,他们在\(v\)的不同子树中(\(v\)自己也算一个子树)。但是这样点对的数量可能达到\(O(n^2)\)。

不过我们仔细思考一下就会发现,其实这样的点对不多。对于一个\(lca\),一个子节点\(v\),我们要与一个在之前已经加入的节点,我们发现,根据贪心,只需要与\(v\)的前驱和后继组合就可以了。

代码:

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define N 200005
  4. using namespace std;
  5. inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
  6. int n,m;
  7. char s[N];
  8. int ch[N<<1][2],fail[N<<1],mxlen[N<<1];
  9. int id[N<<1];
  10. int cnt=1,last=1;
  11. void Insert(int f,int i) {
  12. static int now,p;
  13. now=++cnt;
  14. p=last,last=now;
  15. id[now]=i;
  16. mxlen[now]=mxlen[p]+1;
  17. while(p&&!ch[p][f]) ch[p][f]=now,p=fail[p];
  18. if(!p) return fail[now]=1,void();
  19. int q=ch[p][f];
  20. if(mxlen[q]==mxlen[p]+1) return fail[now]=q,void();
  21. int New=++cnt;
  22. memcpy(ch[New],ch[q],sizeof(ch[q]));
  23. fail[New]=fail[q];
  24. fail[q]=fail[now]=New;
  25. mxlen[New]=mxlen[p]+1;
  26. while(p&&ch[p][f]==q) ch[p][f]=New,p=fail[p];
  27. }
  28. struct load {int to,next;}e[N<<2];
  29. int h[N<<1],edge=1;
  30. void add(int i,int j) {e[++edge]=(load) {j,h[i]};h[i]=edge;}
  31. int val[N<<1];
  32. int size[N<<1],son[N<<1];
  33. void dfs(int v) {
  34. size[v]=1;
  35. for(int i=h[v];i;i=e[i].next) {
  36. int to=e[i].to;
  37. dfs(to);
  38. size[v]+=size[to];
  39. if(size[son[v]]<size[to]) son[v]=to;
  40. }
  41. }
  42. set<int>pos;
  43. set<int>::iterator it;
  44. void statis(int v,int flag) {
  45. if(id[v]) {
  46. if(flag) pos.insert(id[v]);
  47. else pos.erase(id[v]);
  48. }
  49. for(int i=h[v];i;i=e[i].next) {
  50. int to=e[i].to;
  51. statis(to,flag);
  52. }
  53. }
  54. struct node {
  55. int l,r,mx;
  56. bool operator <(const node &a)const {return r<a.r;}
  57. }st[N*50];
  58. int sum;
  59. struct query {
  60. int l,r,id;
  61. bool operator <(const query &a)const {return r<a.r;}
  62. }q[N];
  63. int ans[N];
  64. void cal(int v,int mx) {
  65. if(id[v]) {
  66. it=pos.lower_bound(id[v]);
  67. if(it!=pos.end()) st[++sum]=(node) {id[v],*it,mx};
  68. if(it!=pos.begin()) st[++sum]=(node) {*(--it),id[v],mx};
  69. }
  70. for(int i=h[v];i;i=e[i].next) {
  71. int to=e[i].to;
  72. cal(to,mx);
  73. }
  74. }
  75. void solve(int v,int flag) {
  76. for(int i=h[v];i;i=e[i].next) {
  77. int to=e[i].to;
  78. if(to==son[v]) continue ;
  79. solve(to,0);
  80. }
  81. if(son[v]) solve(son[v],1);
  82. if(id[v]) {
  83. it=pos.lower_bound(id[v]);
  84. if(it!=pos.end()) st[++sum]=(node) {id[v],*it,val[v]};
  85. if(it!=pos.begin()) st[++sum]=(node) {*(--it),id[v],val[v]};
  86. pos.insert(id[v]);
  87. }
  88. for(int i=h[v];i;i=e[i].next) {
  89. int to=e[i].to;
  90. if(to==son[v]) continue ;
  91. cal(to,val[v]);
  92. statis(to,1);
  93. }
  94. if(!flag) pos.clear();
  95. }
  96. void solve2(int v) {
  97. if(id[v]) pos.insert(id[v]);
  98. for(int i=h[v];i;i=e[i].next) {
  99. int to=e[i].to;
  100. solve2(to);
  101. }
  102. for(int i=h[v];i;i=e[i].next) {
  103. int to=e[i].to;
  104. cal(to,val[v]);
  105. statis(to,1);
  106. }
  107. pos.clear();
  108. }
  109. struct Bit {
  110. int tem[N];
  111. int low(int i) {return i&(-i);}
  112. void add(int v,int f) {for(int i=v;i<=n;i+=low(i)) tem[i]=max(tem[i],f);}
  113. int query(int v) {
  114. int ans=0;
  115. for(int i=v;i;i-=low(i)) ans=max(ans,tem[i]);
  116. return ans;
  117. }
  118. }bit;
  119. int main() {
  120. n=Get(),m=Get();
  121. scanf("%s",s+1);
  122. for(int i=1;i<=n;i++) Insert(s[i]-'0',i);
  123. for(int i=2;i<=cnt;i++) {
  124. val[i]=mxlen[i];
  125. add(fail[i],i);
  126. }
  127. dfs(1);
  128. solve(1,1);
  129. sort(st+1,st+1+sum);
  130. for(int i=1;i<=m;i++) q[i].l=Get(),q[i].r=Get(),q[i].id=i;
  131. sort(q+1,q+1+m);
  132. int tag=1;
  133. for(int i=1;i<=m;i++) {
  134. while(tag<=sum&&st[tag].r<=q[i].r) {
  135. bit.add(n-st[tag].l+1,st[tag].mx);
  136. tag++;
  137. }
  138. ans[q[i].id]=bit.query(n-q[i].l+1);
  139. }
  140. for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
  141. return 0;
  142. }

「雅礼集训 2017 Day7」事情的相似度的更多相关文章

  1. 【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度

    Description 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的 ...

  2. 【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度

    题目描述 人的一生不仅要靠自我奋斗,还要考虑到历史的行程. 历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势. 你发现在历史的不同时刻,不断的有相同的事情发 ...

  3. LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度

    我可以大喊一声这就是个套路题吗? 首先看到LCP问题,那么套路的想到SAM(SA的做法也有) LCP的长度是它们在parent树上的LCA(众所周知),所以我们考虑同时统计多个点之间的LCA对 树上问 ...

  4. loj#6041. 「雅礼集训 2017 Day7」事情的相似度(SAM set启发式合并 二维数点)

    题意 题目链接 Sol 只会后缀数组+暴躁莫队套set\(n \sqrt{n} \log n\)但绝对跑不过去. 正解是SAM + set启发式合并 + 二维数点/ SAM + LCT 但是我只会第一 ...

  5. 【loj6041】「雅礼集训 2017 Day7」事情的相似度 后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组

    题目描述 给你一个长度为 $n$ 的01串,$m$ 次询问,每次询问给出 $l$ .$r$ ,求从 $[l,r]$ 中选出两个不同的前缀的最长公共后缀长度的最大值. $n,m\le 10^5$ 题解 ...

  6. loj#6041. 「雅礼集训 2017 Day7」事情的相似度(后缀自动机+启发式合并)

    题面 传送门 题解 为什么成天有人想搞些大新闻 这里写的是\(yyb\)巨巨说的启发式合并的做法(虽然\(LCT\)的做法不知道比它快到哪里去了--) 建出\(SAM\),那么两个前缀的最长公共后缀就 ...

  7. 【LOJ6041】「雅礼集训 2017 Day7」事情的相似度(用LCT维护SAM的parent树)

    点此看题面 大致题意: 给你一个\(01\)串,每次询问前缀编号在一段区间内的两个前缀的最长公共后缀的长度. 离线存储询问 考虑将询问离线,按右端点大小用邻接表存下来(直接排序当然也可以啦). 这样的 ...

  8. LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 LCT+SAM+线段树

    Code: #include<bits/stdc++.h> #define maxn 200003 using namespace std; void setIO(string s) { ...

  9. LOJ6041. 「雅礼集训 2017 Day7」事情的相似度 [后缀树,LCT]

    LOJ 思路 建出反串的后缀树,发现询问就是问一个区间的点的\(lca\)的深度最大值. 一种做法是dfs的时候从下往上合并\(endpos\)集合,发现插入一个点的时候只需要把与前驱后继的贡献算进去 ...

随机推荐

  1. Struts2学习(六)———— 文件上传和下载

    一.单文件上传 在没学struts2之前,我们要写文件上传,非常麻烦,需要手动一步步去获取表单中的各种属性,然后在进行相应的处理,而在struts2中就不需要了,因为有一个fileUpload拦截器帮 ...

  2. 【转载】 禁止国外IP访问你的网站

    在网站的运维过程中,我们通过网站记录的IP列表记录有时候会发现很多国外的IP的访问,如美国的IP等,而很多的服务器攻击行为的发起点很有可能在国外,此时为了服务器安全的考虑,我们可以考虑禁止国外IP访问 ...

  3. js 提交数组到后端(C#)

    JS 代码: <script src="~/Scripts/jquery-1.8.2.min.js"></script> <script> // ...

  4. c# Session写入读取操作

    /// <summary> /// 写Session /// </summary> /// <typeparam name="T">Sessio ...

  5. 探秘小程序(7):view组件

    小程序中最基础,最常用的组件--view,类似于html中div的存在有四个属性: ①hover-class:指定按下去的样式类.当 hover-class="none" 时,没有 ...

  6. SQL 语句在查询分析器执行很快,程序 Dapper 参数化查询就很慢(parameter-sniffing)

    这个问题困扰我好长时间了,使用SQLSERVER 事务探查器找到执行超时的SQL语句,参数查询都是通过执行exe sp_executesql 的存储过程调用,因为它能够分析并缓存查询计划,从而优化查询 ...

  7. hexo博客更新主题后上传Git操作

    克隆主题: git clone https://github.com/SuperKieran/TKL.git _config.yml文件中主题改为新增主题 # Extensions ## Plugin ...

  8. tortoisegit安装

    1.下载tortoisegit:https://tortoisegit.org/download/ 2.下载git 64位 3. 双击开始安装,选择默认,点击下一步 4.接着是选择安装目录,可以保持默 ...

  9. CSS常见布局

    一.单列布局 1. 水平居中 1.1 使用inline-block和text-align .parent{text-align:center;} .child{display:inline-block ...

  10. win10系统彻底卸载Mysql

    本文介绍,在Windows10系统下,如何彻底删除卸载MySQL... 1>停止MySQL服务 开始->所有应用->Windows管理工具->服务,将MySQL服务停止. 2& ...