Description

一条 \(n\) 条边,\(n+1\) 个点的链,边有黑有白。若结点 \(a\) 可以到达 \(b\),需要满足 \(a\to b\) 的路径上的边不能有黑的。现给出 \(0\) 时刻边的初始状态,然后随后 \(1\sim q\) 时刻每时刻有一个事件或查询:

  • \(\texttt{toggle} \ i\):翻转第 \(i\) 条边的颜色(黑 \(\Leftrightarrow\) 白)
  • \(\texttt{query}\ a\ b\):查询从 \(0\) 开始到当前时刻,有多少时刻满足 \(a\) 可以到达 \(b\)。

对于每一个 \(\texttt{query}\),输出答案。

Hint

\(1\le n, q\le 3\times 10^5\)

Solution

设一个位置 \(x\) 可以到达的最左侧位置为 \(L(x)\),右侧为 \(R(x)\)。

这里有一个 set 维护连续段 的技巧——对于边状态 \(\texttt{0100011}\),可以分段存为:\([1, 1], [2, 2], [3,5], [6, 7]\) 四段。对于一个位置 \(x\),所属段为 \([l, r]\),那么有 \(L(x)=l, R(x)= r\)。(好像可以直接线段树)

当修改时,若将边 \(x\to x+1\) 变为白色,那么段 \([L(x), x],[x+1, R(x+1)]\) 间变为连通,可以将这两段删去,用 \([L(x), R(x+1)]\) 取而代之。对答案的影响?显然所有满足 \(a\in [L(x), x],b\in[x+1, R(x+1)]\) 的点对 \((a, b)\) 的答案都需要更改。

设当前时间为 \(t\),总时间为 \(T\)。

考虑如何将一个点对转化成一个二维点,而这又是本题的关键。这样一来,上面的修改变成了 **矩形加 ** 操作——左下点为 \((L(x), x+1)\),右上为 \((x, R(x))\) 的矩形区域的每个位置加上 \(T-t\),表明 暂定后面时刻都是连通的

同理,若是白变黑,那么就是矩形减 \(T-t\)。表明目前看来,后面都不连通。

那么对于查询,只要获取位置 \((a, b)\) 的值即可。若当前两点是联通的,由于上文是暂定,于是还要把后面减掉,答案 \(-(T-t)\)。

如何高效执行上述两个操作?显然可以 CDQ 但我老写炸。不如树套树吧。把矩形修改差分成四个,单点询问转化成区域查询即可。

时空复杂度 \(O(n\log^2 n)\)。

Code

  1. /*
  2. * Author : _Wallace_
  3. * Source : https://www.cnblogs.com/-Wallace-/
  4. * Problem : APIO2019 路灯
  5. */
  6. #include <cstdio>
  7. #include <set>
  8. using namespace std;
  9. const int MaxN = 3e5 + 5;
  10. int n, N, T;
  11. bool dat[MaxN];
  12. char str[MaxN];
  13. namespace bit_seg {
  14. const int S = MaxN << 9;
  15. int lc[S], rc[S], total = 0, sum[S];
  16. #define mid ((l + r) >> 1)
  17. int root[MaxN];
  18. #define lbt(x) (x & (-x))
  19. void ins(int& x, int p, int v, int l, int r) {
  20. if (!x) x = ++total;
  21. sum[x] += v;
  22. if (l == r) return;
  23. if (p <= mid) ins(lc[x], p, v, l, mid);
  24. else ins(rc[x], p, v, mid + 1, r);
  25. }
  26. void upd(int r, int c, int val) {
  27. for (; r <= n; r += lbt(r)) ins(root[r], c, val, 1, n + 1);
  28. }
  29. void rectAdd(int xl, int yl, int xr, int yr, int val) {
  30. upd(xl, yl, val);
  31. upd(xr + 1, yr + 1, val);
  32. upd(xl, yr + 1, -val);
  33. upd(xr + 1, yl, -val);
  34. }
  35. int get(int x, int ql, int qr, int l, int r) {
  36. if (!x) return 0;
  37. if (ql <= l && r <= qr) return sum[x];
  38. int ret = 0;
  39. if (l <= mid) ret += get(lc[x], ql, qr, l, mid);
  40. if (r > mid) ret += get(rc[x], ql, qr, mid + 1, r);
  41. return ret;
  42. }
  43. int Query(int r, int c) {
  44. int ret = 0;
  45. for (; r; r -= lbt(r)) ret += get(root[r], 1, c, 1, n + 1);
  46. return ret;
  47. }
  48. };
  49. struct interval {
  50. int l, r;
  51. inline interval(int L, int R) : l(L), r(R) { }
  52. inline bool operator < (const interval& x) const { return r < x.r; }
  53. };
  54. set<interval> itv;
  55. typedef set<interval>::iterator Iter;
  56. #include <algorithm>
  57. signed main() {
  58. scanf("%d%d", &n, &T), N = n++;
  59. scanf("%s", str + 1);
  60. for (int i = 1; i <= n; i++)
  61. itv.insert(interval(i, i));
  62. for (int i = 1; i <= N; i++) {
  63. dat[i] = (str[i] == '1');
  64. if (dat[i]) {
  65. Iter it = --itv.lower_bound(interval(0, i + 1));
  66. int L = it->l;
  67. itv.erase(it), itv.erase(interval(i + 1, i + 1));
  68. itv.insert(interval(L, i + 1));
  69. }
  70. }
  71. for (Iter it = itv.begin(); it != itv.end(); it++)
  72. bit_seg::rectAdd(it->l, it->l, it->r, it->r, T);
  73. for (int t = 1; t <= T; t++) {
  74. char opt[10];
  75. int i, a, b;
  76. scanf("%s", opt);
  77. if (opt[0] == 't') {
  78. scanf("%d", &i);
  79. if (dat[i]) {
  80. Iter it = itv.lower_bound(interval(0, i));
  81. int l1 = it->l, r1 = i, l2 = i + 1, r2 = it->r;
  82. bit_seg::rectAdd(l1, l2, r1, r2, -(T - t));
  83. itv.erase(interval(l1, r2));
  84. itv.insert(interval(l1, r1));
  85. itv.insert(interval(l2, r2));
  86. } else {
  87. Iter it = itv.lower_bound(interval(0, i));
  88. int l1 = it->l, r1 = i, l2 = i + 1, r2 = (++it)->r;
  89. bit_seg::rectAdd(l1, l2, r1, r2, T - t);
  90. itv.erase(interval(l1, r1));
  91. itv.erase(interval(l2, r2));
  92. itv.insert(interval(l1, r2));
  93. }
  94. dat[i] ^= 1;
  95. } else {
  96. scanf("%d%d", &a, &b);
  97. int ans = bit_seg::Query(a, b);
  98. if (itv.lower_bound(interval(0, a)) == itv.lower_bound(interval(0, b)))
  99. printf("%d\n", ans - (T - t));
  100. else printf("%d\n", ans);
  101. }
  102. }
  103. }

【APIO2019】路灯(ODT & (树套树 | CDQ分治))的更多相关文章

  1. 【LOJ#3146】[APIO2019]路灯(树套树)

    [LOJ#3146][APIO2019]路灯(树套树) 题面 LOJ 题解 考场上因为\(\text{bridge}\)某个\(\text{subtask}\)没有判\(n=1\)的情况导致我卡了\( ...

  2. BZOJ4170 极光(CDQ分治 或 树套树)

    传送门 BZOJ上的题目没有题面-- [样例输入] 3 5 2 4 3 Query 2 2 Modify 1 3 Query 2 2 Modify 1 2 Query 1 1 [样例输出] 2 3 3 ...

  3. 【Bzoj 3295】 动态逆序对(树套树|CDQ分治)

    [题意] 每次删除一个数,然后问删除前逆序对数. [分析] 没有AC不开心.. 我的树状数组套字母树,应该是爆空间的,空间复杂度O(nlogn^2)啊..哭.. 然后就没有然后了,别人家的树套树是树状 ...

  4. bzoj3110: [Zjoi2013]K大数查询 【cdq分治&树套树】

    模板题,折腾了许久. cqd分治整体二分,感觉像是把询问分到答案上. #include <bits/stdc++.h> #define rep(i, a, b) for (int i = ...

  5. bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

    Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...

  6. [BZOJ3295][Cqoi2011]动态逆序对 CDQ分治&树套树

    3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Description 对于序列A,它的逆序对数定义为满足i<j,且 ...

  7. Codechef-ANCESTOR(树套树/CDQ分治)

    题意: 给定两棵有根树,各有 N 个点.两棵树上的点分别被从 1 到 N 标号.两棵树的根均为标号为 1 的节点. 你的任务非常简单:对于每个 i,找到一个 j(j != i),使得在两棵树中 j 都 ...

  8. BZOJ 3262 cdq分治 OR 树套树

    注意判断 三个条件都一样的-- (CDQ分治 其实并不是很难理解 只是想不到--) CDQ分治: //By SiriusRen #include <cstdio> #include < ...

  9. P5445 [APIO2019]路灯(树套树)

    P5445 [APIO2019]路灯 转化为平面上的坐标(x,y),set维护连续区间. 用树套树维护矩阵加法,单点查询. 注意维护矩阵差分的时候, $(x,y,v)$是对$(x,y)(n+1,n+1 ...

随机推荐

  1. python 迭代器(转)

    迭代器 迭代器是在python2.2中被加入的,它为类序列对象提供了一个类序列的接口.有了迭代器可以迭代一个不是序列的对象,因为他表现出了序列的行为.当在python中使用for循环迭代一个对象时,调 ...

  2. 头秃了,二十三张图带你从源码了解Spring Boot 的启动流程~

    持续原创输出,点击上方蓝字关注我 目录 前言 源码版本 从哪入手? 源码如何切分? 如何创建SpringApplication? 设置应用类型 设置初始化器(Initializer) 设置监听器(Li ...

  3. git-新建git用户流程-1

    1.输入:https://git-scm.com/ 2.点击try git 4.注册git填写用户名和密码,邮箱,验证邮箱 5.选择免费的版本 6.创建仓库名称 创建成功见截图

  4. Redis分布式锁的正确使用与实现原理

    模拟一个电商里面下单减库存的场景. 1.首先在redis里加入商品库存数量. 2.新建一个Spring Boot项目,在pom里面引入相关的依赖. <dependency> <gro ...

  5. Linux(CentOS6.8)配置Redis

    1.Redis简介 Redis:REmote DIctionary Server(远程字典服务器). Redis是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(key/value)分 ...

  6. Java 开发之 Lombok 必知必会

    转载链接地址:https://juejin.im/post/5cf3edf7e51d454f71439c79 1. 前言 在目前众多编程语言中,Java 语言的表现还是抢眼,不论是企业级服务端开发,还 ...

  7. 我要进大厂之大数据MapReduce知识点(1)

    01 我们一起学大数据 老刘今天分享的是大数据Hadoop框架中的分布式计算MapReduce模块,MapReduce知识点有很多,大家需要耐心看,用心记,这次先分享出MapReduce的第一部分.老 ...

  8. pip install 一个本地包时提示error: Microsoft Visual C++ 14.0 is required.

    错误如下: error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Too ...

  9. 重新认识Lombok

    Lombok插件 简介 那么lombok到底是个什么呢,lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具,简单来说,比如我们新建了一个类,然后 ...

  10. Java进阶专题(十七) 系统缓存架构设计 (上)

    前言 ​ 我们将先从Redis.Nginx+Lua等技术点出发,了解缓存应用的场景.通过使用缓存相关技术,解决高并发的业务场景案例,来深入理解一套成熟的企业级缓存架构如何设计的.本文Redis部分总结 ...