题目描述

给你一个长度为 $n$ 的01串,$m$ 次询问,每次询问给出 $l$ 、$r$ ,求从 $[l,r]$ 中选出两个不同的前缀的最长公共后缀长度的最大值。

$n,m\le 10^5$


题解

后缀自动机+STL-set+启发式合并+离线+扫描线+树状数组

两个前缀的最长公共后缀,在正串后缀自动机上体现为pre树上两点LCA的深度。

考虑统计pre树上一个点的贡献:对于两个前缀 $x$ 、$y$ ,它能够影响的询问左端点小于等于 $x$ ,右端点大于等于 $y$ 。因此影响最大化的前缀对就是排序后相邻的两个,每次只需要考虑这些前缀对。

那么我们考虑两个子树合并的过程,使用STL-set维护前驱后继成为贡献。这个过程可以启发式合并,把小的合并到大的中。

剩下的就是对于询问 $[l,r]$ ,询问前缀对中第一个 $\ge l$ ,第二个 $\le r$ 的最大值。离线+扫描线+树状数组维护前缀最小值即可。

时间复杂度瓶颈在于启发式合并STL-set,为 $O(n\log^2n)$ 。

  1. #include <set>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #define N 200010
  6. using namespace std;
  7. struct data
  8. {
  9. int x , y , z;
  10. data() {}
  11. data(int a , int b , int c) {x = a , y = b , z = c;}
  12. bool operator<(const data &a)const {return x < a.x;}
  13. }a[N * 20] , q[N];
  14. set<int> s[N];
  15. int pre[N] , c[N][2] , dis[N] , val[N] , last = 1 , tot = 1 , head[N] , to[N] , next[N] , cnt , bl[N] , ta , f[N] , n , ans[N];
  16. char str[N];
  17. void insert(int k , int ch)
  18. {
  19. int p = last , np = last = ++tot;
  20. dis[np] = dis[p] + 1 , s[np].insert(k);
  21. while(p && !c[p][ch]) c[p][ch] = np , p = pre[p];
  22. if(!p) pre[np] = 1;
  23. else
  24. {
  25. int q = c[p][ch];
  26. if(dis[q] == dis[p] + 1) pre[np] = q;
  27. else
  28. {
  29. int nq = ++tot;
  30. memcpy(c[nq] , c[q] , sizeof(c[q]));
  31. dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[np] = pre[q] = nq;
  32. while(p && c[p][ch] == q) c[p][ch] = nq , p = pre[p];
  33. }
  34. }
  35. }
  36. inline void add(int x , int y)
  37. {
  38. to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
  39. }
  40. void dfs(int x)
  41. {
  42. int i;
  43. set<int>::iterator j , k;
  44. for(i = head[x] ; i ; i = next[i])
  45. {
  46. dfs(to[i]);
  47. if(s[bl[x]].size() > s[bl[to[i]]].size())
  48. {
  49. for(j = s[bl[to[i]]].begin() ; j != s[bl[to[i]]].end() ; j ++ )
  50. {
  51. k = s[bl[x]].upper_bound(*j);
  52. if(k != s[bl[x]].end()) a[++ta] = data(*j , *k , dis[x]);
  53. if(k != s[bl[x]].begin()) a[++ta] = data(*--k , *j , dis[x]);
  54. s[bl[x]].insert(*j);
  55. }
  56. }
  57. else
  58. {
  59. for(j = s[bl[x]].begin() ; j != s[bl[x]].end() ; j ++ )
  60. {
  61. k = s[bl[to[i]]].upper_bound(*j);
  62. if(k != s[bl[to[i]]].end()) a[++ta] = data(*j , *k , dis[x]);
  63. if(k != s[bl[to[i]]].begin()) a[++ta] = data(*--k , *j , dis[x]);
  64. s[bl[to[i]]].insert(*j);
  65. }
  66. bl[x] = bl[to[i]];
  67. }
  68. }
  69. }
  70. inline void fix(int x , int a)
  71. {
  72. int i;
  73. for(i = x ; i <= n ; i += i & -i) f[i] = max(f[i] , a);
  74. }
  75. inline int query(int x)
  76. {
  77. int i , ans = 0;
  78. for(i = x ; i ; i -= i & -i) ans = max(ans , f[i]);
  79. return ans;
  80. }
  81. int main()
  82. {
  83. int m , i , p;
  84. scanf("%d%d%s" , &n , &m , str + 1);
  85. for(i = 1 ; i <= n ; i ++ ) insert(i , str[i] - '0');
  86. for(i = 2 ; i <= tot ; i ++ ) add(pre[i] , i);
  87. for(i = 1 ; i <= tot ; i ++ ) bl[i] = i;
  88. dfs(1);
  89. for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &q[i].x , &q[i].y) , q[i].z = i;
  90. sort(a + 1 , a + ta + 1) , sort(q + 1 , q + m + 1);
  91. for(p = ta , i = m ; i ; i -- )
  92. {
  93. while(p && a[p].x >= q[i].x) fix(a[p].y , a[p].z) , p -- ;
  94. ans[q[i].z] = query(q[i].y);
  95. }
  96. for(i = 1 ; i <= m ; i ++ ) printf("%d\n" , ans[i]);
  97. return 0;
  98. }

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

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

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

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

    「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 20155318 2016-2017-2 《Java程序设计》第一周学习总结

    20155318 2016-2017-2 <Java程序设计>第一周学习总结 教材学习内容总结 上周总结 上周学习了一些大学的学习方法,比如知识分为为三种:元知识.软知识和硬知识,讲述技能 ...

  2. angular中的$q服务实例

    用于理解$q服务 参考:http://www.zouyesheng.com/angular.html#toc39 广义回调管理 和其它框架一样, ng 提供了广义的异步回调管理的机制. $http 服 ...

  3. Drupal7重置密码方法

    Drupal版本 7.40 方法1: 根目录index.php添加 require_once 'includes/password.inc'; require_once 'includes/boots ...

  4. springmvc 使用 response 的注意事项以及解决500 空指针异常找不到 response 的方法

    使用注解方式在类中(Controller)来装载request时,是可以正常使用request的(必须在启动时才注入,所以不支持热部署),但是同样使用这种方式在已经装载了 request的情况下装载  ...

  5. 一、EnterpriseFrameWork框架总体介绍

    EnterpriseFrameWork框架是自己在工作之余的得意之作,经过了几年时间的不断重构,现在终于有了现在的样子:刚开始只是为了方便开发WEB系统,随着项目越做越多,新的功能也就不断补充进去,补 ...

  6. 在腾讯云上安装mysql遇到的问题

    卸载mysql: 1.sudo apt-get autoremove --purge mysql-server-5.5 5.5 是数据库版本, mysql -v 显示版本信息 2.sudo apt-g ...

  7. AndroidStudio 新建不同的Drawable文件夹

    以前习惯eclipse开发Android的朋友们知道 新创建一个Android项目的时候eclipse会自动生成多个drawable文件夹来存放图片 但是Android Studio 新建项目的时候只 ...

  8. 测试Websocket建立通信,使用protobuf格式交换数据

    接到一个应用测试,应用实现主要使用websocket保持长链接,使用protobuf格式交换数据,用途为发送消息,需要我们测试评估性能,初步评估需要测试长链接数.峰值消息数以及长期运行稳定性 整体需求 ...

  9. Java EE JSP内置对象及表达式语言

    一.JSP内置对象 JSP根据Servlet API规范提供了一些内置对象,开发者不用事先声明就可使用标准变量来访问这些对象. JSP提供了9种内置对象: (一).request 简述: JSP编程中 ...

  10. Linux内核学习笔记(2)-- 父进程和子进程及它们的访问方法

    Linux系统中,进程之间有一个明显的继承关系,所有进程都是 PID 为1的 init 进程的后代.内核在系统启动的最后阶段启动 init 进程.该进程读取系统的初始化脚本(initscript)并执 ...