单调栈+set+后缀数组

一道奇妙的题

这道题如果对于每个询问$r$是固定的,那么就很简单了,可惜并不是

由于r会变化,那么对于两个子串$[i...r],[j...r]$,他们的大小关系随着r的变化也会变化,使得我们不能直接预处理答案

所以我们把询问离线,把每个询问按照r分类,通过考虑r的变化来完成询问

对于两个后缀$[i...r],[j...r],(i<j)$,设他们的$lcp=l$,那么当$l∈[j,j+l-1]$时,$[i...r]>[j...r]$,当$r=j+l$时,则变成$[i...r]<[j...r]$,因为$s[i+l]<s[j+l]$(假设)

我们称这样的两个后缀为$i$伴随$j$。

那么我们对答案维护一个set,里面存着一些后缀,保证后缀当前的大小单调递减,这样我们就可以每次快速询问答案。

但是由于$r$的变化每次大小关系会改变,也就是我们要把一些后缀删掉

那么我们再维护一个单调栈,这里存的是严格的后缀大小,这个单调栈是为了给每个新加入的后缀求出哪些后缀伴随他

那么如果当前的栈顶是大于新的后缀,$break$,否则标记$top$伴随$i$,并且记录当$i$,也就是$r=i+lcp$时删除栈顶

每次删除一个元素时,伴随他的元素也应该删除,所以每次$dfs$删除即可,并在$set$里删除

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. #include <set>
  5. #include <vector>
  6. using namespace std;
  7. const int N = 1e5 + ;
  8. int n, k, m, top;
  9. int rnk[N], tmp[N], sa[N], ans[N], vis[N], st[N], h[N][], Log[N];
  10. char s[N];
  11. set<int> S;
  12. vector<pair<int, int> > v[N];
  13. vector<int> del[N], g[N];
  14. bool cmp(int i, int j) {
  15. if(rnk[i] != rnk[j]) {
  16. return rnk[i] < rnk[j];
  17. }
  18. int ri = i + k <= n ? rnk[i + k] : -;
  19. int rj = j + k <= n ? rnk[j + k] : -;
  20. return ri < rj;
  21. }
  22. void dfs(int u) {
  23. vis[u] = ;
  24. S.erase(u);
  25. for(int i = ; i < g[u].size(); ++i) {
  26. if(!vis[g[u][i]]) {
  27. dfs(g[u][i]);
  28. }
  29. }
  30. }
  31. void SA() {
  32. for(int i = ; i <= n; ++i) {
  33. sa[i] = i;
  34. rnk[i] = s[i];
  35. }
  36. for(k = ; k <= n; k <<= ) {
  37. sort(sa + , sa + n + , cmp);
  38. tmp[sa[]] = ;
  39. for(int i = ; i <= n; ++i) {
  40. tmp[sa[i]] = tmp[sa[i - ]] + (cmp(sa[i - ], sa[i]));
  41. }
  42. for(int i = ; i <= n; ++i) {
  43. rnk[i] = tmp[i];
  44. }
  45. }
  46. for(int i = ; i <= n; ++i) {
  47. rnk[sa[i]] = i;
  48. }
  49. int d = ;
  50. for(int i = ; i <= n; ++i) {
  51. if(rnk[i] <= ) {
  52. continue;
  53. }
  54. int j = sa[rnk[i] - ];
  55. if(d) {
  56. --d;
  57. }
  58. for(; i + d <= n && j + d <= n; ++d) {
  59. if(s[i + d] != s[j + d]) {
  60. break;
  61. }
  62. }
  63. h[rnk[i] - ][] = d;
  64. }
  65. for(int j = ; j <= ; ++j) {
  66. for(int i = ; i + ( << j) - <= n; ++i) {
  67. h[i][j] = min(h[i][j - ], h[i + ( << (j - ))][j - ]);
  68. }
  69. }
  70. }
  71. int rmq(int l, int r) {
  72. if(l == r) {
  73. return n - l + ;
  74. }
  75. l = rnk[l];
  76. r = rnk[r];
  77. if(l > r) {
  78. swap(l, r);
  79. }
  80. --r;
  81. int x = Log[r - l + ];
  82. return min(h[l][x], h[r - ( << x) + ][x]);
  83. }
  84. int main() {
  85. scanf("%s%d", s + , &m);
  86. n = strlen(s + );
  87. for(int i = ; i <= n; ++i) {
  88. Log[i] = Log[i >> ] + ;
  89. }
  90. SA();
  91. for(int i = ; i <= m; ++i) {
  92. int l, r;
  93. scanf("%d%d", &l, &r);
  94. v[r].push_back(make_pair(l, i));
  95. }
  96. for(int i = ; i <= n; ++i) {
  97. S.insert(i);
  98. while(top) {
  99. int lcp = rmq(st[top], i);
  100. if(s[st[top] + lcp] > s[i + lcp]) {
  101. break;
  102. }
  103. del[i + lcp].push_back(st[top]);
  104. g[i].push_back(st[top]);
  105. --top;
  106. }
  107. st[++top] = i;
  108. for(int j = ; j < del[i].size(); ++j) {
  109. if(!vis[del[i][j]]) {
  110. dfs(del[i][j]);
  111. }
  112. }
  113. for(int j = ; j < v[i].size(); ++j) {
  114. pair<int, int> x = v[i][j];
  115. ans[x.second] = *S.lower_bound(x.first);
  116. }
  117. }
  118. for(int i = ; i <= m; ++i) {
  119. printf("%d\n", ans[i]);
  120. }
  121. return ;
  122. }

bzoj4453的更多相关文章

  1. 【BZOJ4453】cys就是要拿英魂! 后缀数组+单调栈+set

    [BZOJ4453]cys就是要拿英魂! Description pps又开始dota视频直播了!一群每天被pps虐的蒟蒻决定学习pps的操作技术,他们把pps在这局放的技能记录了下来,每个技能用一个 ...

随机推荐

  1. cocos2d-x AssetsManager libcurl使用心得

    libcurl使用心得 最新正在写cocosclient更新的逻辑.研究了一下cocos2d-x自带的Libcurl,下面是自己在使用过程中的心得和遇到的未解问题.希望大家一起讨论一下,欢迎大家指导. ...

  2. ABAP 弹出框 函数

    POPUP_GET_VALUES_USER_HELP 是一个和用户交互信息的函数,用户能够填写信息,并且我们还能够依据实际的需求对弹出框进行F1 F4 以及用户的需求进行增强.具体的实现能够參考系统标 ...

  3. 九度OJ 1078:二叉树遍历 (二叉树)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3748 解决:2263 题目描述: 二叉树的前序.中序.后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树 ...

  4. 九度OJ 1076:N的阶乘 (数字特性、大数运算)

    时间限制:3 秒 内存限制:128 兆 特殊判题:否 提交:6384 解决:2238 题目描述: 输入一个正整数N,输出N的阶乘. 输入: 正整数N(0<=N<=1000) 输出: 输入可 ...

  5. centos 7 官网安装 PostgreSQL

    https://www.postgresql.org/download/linux/redhat/

  6. php微信支付测试开发(流程已通)

    必要条件: appid //公众号后台开发者中心获得(和邮件内的一样)   mchid//邮件内获得  key//商户后台自己设置  appsecret //公众号开发者中心获得 两个证书文件,邮件内 ...

  7. 《Python Machine Learning》索引

    目录部分: 第一章:赋予计算机从数据中学习的能力 第二章:训练简单的机器学习算法——分类 第三章:使用sklearn训练机器学习分类器 第四章:建立好的训练集——数据预处理 第五章:通过降维压缩数据 ...

  8. bind(),live(),delegate(),on()绑定事件方式

    1.bind():向匹配元素添加一个或多个事件处理器. 适用所有版本,但是自从jquery1.7版本以后bind()函数推荐用on()来代替. $(selector).bind(event,data, ...

  9. YxdJSON - Delphi 高性能 JSON 库(支持RTTI和序列化操作)

    源:YxdJSON - Delphi 高性能 JSON 库(支持RTTI和序列化操作) Delphi 高性能 JSON 库(支持RTTI和序列化操作) 支持平台: Windows, Android, ...

  10. nodejs中的子进程,深入解析child_process模块和cluster模块

    Node.js的进程管理   node遵循的是单线程单进程的模式,node的单线程是指js的引擎只有一个实例,且在nodejs的主线程中执行,同时node以事件驱动的方式处理IO等异步操作.node的 ...