题目

题意简述

  维护一个长度为 \(n\) 的序列 \(\{a_n\}\),并给出 \(q\) 个操作:

  • 将下标为 \(x\) 的数修改为 \(y\)。
  • 给定 \(l,r,k\),求最大的 \(m\) 使得 \(\{k,k+1,\dots,k+m\}\) 是区间 \([l,r]\) 内元素的子序列。

数据规模

  \(n,q\le10^6;~a_i,y,k\le n\)。

题解

  首先不考虑修改操作,如何处理询问呢?

  不难想到维护一列指针。令 \(suf_i\) 为 \(i\) 之后第一个满足 \(a_j=a_i+1\) 的 \(j\)。那么对于询问,相当于求从区间内的第一个 \(k\) 作为链头,链尾不超过 \(r\) 的链长。可以用 std::set 维护每个值出现的下标,再预处理出 \(suf\) 链的倍增,做到 \(O(n\log n)-O\left(q\log n\right)\) 求解。

  这时再引入修改,自然地想到用动态树来维护一下 \(suf\) 链的形状。不过时间复杂度却因如下数据无法保证:若有 \(a=\{1,1,\dots,1,2\}\),则 \(suf=\{n,n,\dots,n,/\}\),再修改 \(a_n\) 的值,发现 \(suf_1\) 到 \(suf_{n-1}\) 的值都需要被修改啦!

  为应对这一情况,我们放宽 \(suf\) 的限制,令 \(suf_i\) 为 \(i\) 之后第一个满足 \(a_j\in\{a_i,a_i+1\}\) 的 \(j\),并为链加上边权:当 \(a_j=a_i\),没有贡献,边权为 \(0\),否则边权为 \(1\)。实际上,由于每个点仅会向右连一条边,可以把 \(suf_i\) 边权作为 \(i\) 的点权。并加入虚拟结点 \(n+1\) 作为动态树的根,对于任意 \(suf_i\),若没有满足条件的 \(j\),则它指向 \(n+1\)。

  这样一来,我们就有能力处理修改与查询啦。

  • 查询操作:先找到区间中的第一个 \(k\) 所对应的下标 \(u\),在 LCT 中提取 \(u\rightarrow n+1\) 这条路径(由 \(suf\) 的定义,一定存在),并在路径 Splay 上查找 \(r\) 的前驱结点 \(v\) ——由于 \(n+1\) 为根,所以深度越浅,结点编号越大,所以查前驱的时候需要把 Splay 当做“左大右小”的平衡树。最后,提取路径 \(u\rightarrow v\),计算子树和,注意判断 \(v\) 结点是否产生贡献。
  • 修改操作:分别考虑去掉该点所产生的印象和加入该点所产生的印象。利用 std::set 进行 lower_bound 等操作即可。

代码

  由于码的时候思路比较混乱,所以没有封装,而且压行有些丑呢 qwq。

  1. #include <set>
  2. #include <cstdio>
  3. #include <assert.h>
  4. #include <iostream>
  5. typedef std :: set<int> :: iterator IT;
  6. inline int rint () {
  7. int x = 0, f = 1; char s = getchar ();
  8. for ( ; s < '0' || '9' < s; s = getchar () ) f = s == '-' ? -f : f;
  9. for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
  10. return x * f;
  11. }
  12. template<typename Tp>
  13. inline void wint ( Tp x ) {
  14. if ( x < 0 ) putchar ( '-' ), x = ~ x + 1;
  15. if ( 9 < x ) wint ( x / 10 );
  16. putchar ( x % 10 ^ '0' );
  17. }
  18. const int MAXN = 2e6;
  19. int n, q, a[MAXN + 5], suf[MAXN + 5];
  20. std :: set<int> apr[MAXN + 5];
  21. int ch[MAXN + 5][2], fa[MAXN + 5], val[MAXN + 5], siz[MAXN + 5], sum[MAXN + 5];
  22. bool flip[MAXN + 5];
  23. inline bool nroot ( const int x ) { return ch[fa[x]][0] == x || ch[fa[x]][1] == x; }
  24. inline void pushrv ( const int x ) { flip[x] ^= 1, ch[x][0] ^= ch[x][1] ^= ch[x][0] ^= ch[x][1]; }
  25. inline void pushup ( const int x ) { sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x]; }
  26. inline void pushdn ( const int x ) {
  27. if ( ! flip[x] ) return ;
  28. if ( ch[x][0] ) pushrv ( ch[x][0] );
  29. if ( ch[x][1] ) pushrv ( ch[x][1] );
  30. flip[x] = false;
  31. }
  32. inline void rotate ( const int x ) {
  33. int y = fa[x], z = fa[y], k = ch[y][1] == x;
  34. fa[x] = z; if ( nroot ( y ) ) ch[z][ch[z][1] == y] = x;
  35. ch[y][k] = ch[x][k ^ 1]; if ( ch[x][k ^ 1] ) fa[ch[x][k ^ 1]] = y;
  36. ch[x][k ^ 1] = y, fa[y] = x, pushup ( y ), pushup ( x );
  37. }
  38. inline void splay ( const int x ) {
  39. static int y, z, stk[MAXN + 5];
  40. for ( stk[z = 1] = y = x; nroot ( y ); stk[++ z] = y = fa[y] );
  41. for ( ; z; pushdn ( stk[z --] ) );
  42. for ( ; nroot ( x ); rotate ( x ) ) {
  43. if ( nroot ( y = fa[x] ) ) {
  44. rotate ( x ^ y ^ ch[y][0] ^ ch[fa[y]][0] ? x : y );
  45. }
  46. }
  47. pushup ( x );
  48. }
  49. inline void access ( int x ) { for ( int y = 0; x; x = fa[y = x] ) splay ( x ), ch[x][1] = y, pushup ( x ); }
  50. inline void makeRoot ( const int x ) { access ( x ), splay ( x ), pushrv ( x ); }
  51. inline void link ( const int x, const int y ) { makeRoot ( x ), fa[x] = y; }
  52. inline void cut ( const int x, const int y ) { makeRoot ( x ), access ( y ), splay ( x ), fa[y] = ch[x][1] = 0, pushup ( x ); }
  53. inline void find ( int& u, const int k ) { for ( pushdn ( u ); ch[u][k < u] && k ^ u; pushdn ( u = ch[u][k < u] ) ); splay ( u ); }
  54. inline int getPre ( int u, const int k ) {
  55. find ( u, k ); if ( u <= k ) return u;
  56. pushdn ( u ), u = ch[u][1];
  57. for ( pushdn ( u ); ch[u][0]; u = ch[u][0], pushdn ( u ) );
  58. return u;
  59. }
  60. inline void linksuf ( const int i, const int k ) {
  61. IT it1 ( apr[k].upper_bound ( i ) ), it2 ( apr[k + 1].upper_bound ( i ) );
  62. suf[i] = std :: min ( *it1, *it2 );
  63. val[i] = suf[i] == *it2, pushup ( i ), link ( i, suf[i] );
  64. }
  65. inline void relink ( const int i, const int k ) {
  66. IT it = apr[k].lower_bound ( i );
  67. if ( it != apr[k].begin () ) {
  68. -- it;
  69. cut ( *it, suf[*it] );
  70. linksuf ( *it, a[*it] );
  71. }
  72. }
  73. int main () {
  74. n = rint (), q = rint ();
  75. for ( int i = 1; i <= n; ++ i ) apr[a[i] = rint ()].insert ( i );
  76. for ( int i = 0; i <= n + 1; ++ i ) apr[i].insert ( n + 1 );
  77. val[n + 1] = 0, pushup ( n + 1 );
  78. for ( int i = 1; i <= n; ++ i ) linksuf ( i, a[i] );
  79. for ( int opt, l, r; q --; ) {
  80. opt = rint (), l = rint (), r = rint ();
  81. if ( opt & 1 ) {
  82. int t = a[l]; a[l] = r;
  83. apr[t].erase ( l ), apr[r].insert ( l );
  84. relink ( l, t ), relink ( l, t - 1 ), relink ( l, r ), relink ( l, r - 1 );
  85. cut ( l, suf[l] ), linksuf ( l, a[l] );
  86. } else {
  87. int p = *apr[rint ()].lower_bound ( l ), q;
  88. if ( p > r ) { puts ( "-1" ); continue; }
  89. makeRoot ( n + 1 ), access ( p ), splay ( p );
  90. q = getPre ( p, r );
  91. makeRoot ( p ), access ( q ), splay ( q );
  92. wint ( sum[q] - val[q] ), putchar ( '\n' );
  93. }
  94. }
  95. return 0;
  96. }

Solution -「NOI.AC 省选膜你赛」array的更多相关文章

  1. Solution -「NOI.AC 省选膜你赛」T2

      这道题就叫 T2 我有什么办法www 题目 题意简述   给定一个字符串 \(s\),其长度为 \(n\),求无序子串对 \((u,v)\) 的个数,其中 \((u,v)\) 满足 \(u,v\) ...

  2. Solution -「NOI.AC 省选膜你赛」寄蒜几盒

    题目 题意简述   给定一个含有 \(n\) 个顶点的凸多边形( \(n\) 是偶数),对于每一对相对的边(即中间有 \(\frac{n}2-1\) 条其它边),延长它们以将平面分割为多块,并把包含原 ...

  3. Solution -「NOI.AC 省选膜你赛」union

    题目 题意简述   给定两颗树 \(A,B\),\(A\) 中的任一结点 \(u\) 与 \(B\) 中的任一结点 \(v\) 都有一个关系值 \(f(u,v)\),初始为 \(0\).再给出 \(q ...

  4. cdcqの省选膜你赛

    cdcqの省选膜你赛 比赛当天因为在杠hnoi2016的大数据结构没有参加,今天补了一下.挺好玩的虽然不看一句话题意的话真的卡读题 此生无悔入东方,来世愿生幻想乡 2651. 新史「新幻想史 -现代史 ...

  5. Solution -「NOI 2021」「洛谷 P7740」机器人游戏

    \(\mathcal{Description}\)   Link.   自己去读题面叭~ \(\mathcal{Solution}\)   首先,参悟[样例解释 #2].一种暴力的思路即为钦定集合 \ ...

  6. Solution -「NOI 2020」「洛谷 P6776」超现实树

    \(\mathcal{Description}\)   Link.   对于非空二叉树 \(T\),定义 \(\operatorname{grow}(T)\) 为所有能通过若干次"替换 \( ...

  7. Solution -「NOI 模拟赛」彩色挂饰

    \(\mathcal{Description}\)   给定一个含 \(n\) 个点 \(m\) 条边的简单无向图,设图中最大点双的大小为 \(s\),则保证 \(s\le6\).你将要用 \(k\) ...

  8. Solution -「NOI 模拟赛」出题人

    \(\mathcal{Description}\)   给定 \(\{a_n\}\),求一个 \(\{b_{n-1}\}\),使得 \(\forall x\in\{a_n\},\exists i,j\ ...

  9. Solution -「NOI 2016」「洛谷 P1587」循环之美

    \(\mathcal{Description}\)   Link.   给定 \(n,m,k\),求 \(x\in [1,n]\cap\mathbb N,y\in [1,m]\cap \mathbb ...

随机推荐

  1. 面试题68 - II. 二叉树的最近公共祖先

    <搜索树结点> <获取路径> 题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q ...

  2. VC 2010 Express 学生版(中文版)

    Microsoft Visual C++ 2010 Express 学生版 下载传送门(提取码:r7sm) 如何安装 拿到压缩文件后,解压到桌面(别怕,安装完后这个文件夹是可以删除的). 在 &quo ...

  3. Go语言系列之自定义实现日志库

    日志库logo gitee地址传送门:https://gitee.com/zhangyafeii/logo 日志库需求分析 1. 支持往不同的地方输出日志 2. 日志分级别 Debug Trace I ...

  4. php反序列化之pop链构造

    本题是某信有一次内部比赛的题目,涉及到pop链的构造问题,所以在这里与大家分享一下 题目 查看源码 逻辑是当参数fn存在且不包含string.zlib.flag这三个字符串时,进行文件包含这里的过滤是 ...

  5. Cesium入门7 - Adding Terrain - 添加地形

    Cesium入门7 - Adding Terrain - 添加地形 Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com ...

  6. 集合框架-TreeSet-Comparator比较器练习(字符串长度排序)

    1 package cn.itcast.p5.treeset.test; 2 3 import java.util.Iterator; 4 import java.util.TreeSet; 5 6 ...

  7. java-异常-finally代码块

    1 package p1.exception; 2 3 4 class Demo_0{ 5 public int show(int index) throws ArrayIndexOutOfBound ...

  8. 第03讲:Flink 的编程模型与其他框架比较

    Flink系列文章 第01讲:Flink 的应用场景和架构模型 第02讲:Flink 入门程序 WordCount 和 SQL 实现 第03讲:Flink 的编程模型与其他框架比较 本课时我们主要介绍 ...

  9. 第06讲:Flink 集群安装部署和 HA 配置

    Flink系列文章 第01讲:Flink 的应用场景和架构模型 第02讲:Flink 入门程序 WordCount 和 SQL 实现 第03讲:Flink 的编程模型与其他框架比较 第04讲:Flin ...

  10. windows系统配置Nginx使用SSL证书实现Https反向代理

    Nginx反向代理服务,可以代理接收请求,并把请求转发到设置好的其他服务器上. 例如,Nginx服务器为 100.101.102.103,A服务为 100.101.102.104 ,通过Nginx配置 ...