传送门

题解

题面大意:

\(0.\)区间加节点

\(1.\)区间换根

\(2.\)单点询问距离

如果没有\(1\)操作,因为区间加节点都是加在下面,所以我们可以直接把\(n\)棵树压成一棵树,直接询问即可

有\(1\)操作怎么办?

上面挖掘了一点性质,

加节点加在下面,那么我们可以先把节点都加上去,再询问

那么把操作离线,

先按操作位置排序,再按操作排序(\(0,1\)先),再按时间排序

对于\(0,1\)操作都新建节点

\(0\)建实点

\(1\)建虚点

\(0\)操作的点将连向最后的\(1\)操作

默认每个\(1\)操作连向上一个操作(加点直接加在\(1\)下面)

现在唯一的问题即是\(1\)操作

我们想一下\(pos\)转移到\(pos+1\)

由于一些换根操作

树的形态会发生改变

假如一个换根操作\([l,r]\)

\(x\)换到\(y\)

\(l-1\),根是\(x\)

\(l\),根是\(y\)

那么改变的地方就是把在\(x->y\)操作之后接上\(x\)的点,全部接到\(y\)下面

一个一个挪肯定不行

所以需要一个虚点,挪的话只要挪这一个点

如果没有理解,可以想想,哪些点会连向这个虚点?

一定是时间在它之后的点

没换根之前,这些点都会连向\(x\)

那么问题就解决了..

Code

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. #define RG register
  4. using namespace std;
  5. template<class T> inline void read(T &x) {
  6. x = 0; RG char c = getchar(); bool f = 0;
  7. while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
  8. while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
  9. x = f ? -x : x;
  10. return ;
  11. }
  12. template<class T> inline void write(T x) {
  13. if (!x) {putchar(48);return ;}
  14. if (x < 0) x = -x, putchar('-');
  15. int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
  16. for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
  17. }
  18. const int N = 500010;
  19. int ch[N][2], val[N], sum[N], fa[N], tot;
  20. bool isroot(int x) { return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }
  21. #define get(x) (ch[fa[x]][1] == x)
  22. void pushup(int x) { sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x]; }
  23. void rotate(int x) {
  24. int y = fa[x], z = fa[y], k = get(x);
  25. if (!isroot(y)) ch[z][get(y)] = x; fa[x] = z;
  26. ch[y][k] = ch[x][k ^ 1]; fa[ch[x][k ^ 1]] = y;
  27. ch[x][k ^ 1] = y; fa[y] = x;
  28. pushup(y);
  29. }
  30. void splay(int x) {
  31. while (!isroot(x)) {
  32. int y = fa[x];
  33. if (!isroot(y))
  34. (get(x) ^ get(y)) ? rotate(x) : rotate(y);
  35. rotate(x);
  36. }
  37. pushup(x);
  38. }
  39. int access(int x) {
  40. int y = 0; for (; x; y = x, x = fa[x]) splay(x), ch[x][1] = y, pushup(x);
  41. return y;
  42. }
  43. void link(int x, int y) { access(x); splay(x); fa[x] = y; }
  44. void cut(int x) { access(x); splay(x); ch[x][0] = fa[ch[x][0]] = 0; pushup(x); }
  45. void newnode(int x) { val[++tot] = x; sum[tot] = x; }
  46. int L[N], R[N], id[N], len;
  47. struct node {
  48. int pos, op, x, y;
  49. bool operator < (const node &z) const {
  50. return pos == z.pos ? op < z.op : pos < z.pos;
  51. }
  52. }q[N];
  53. int ans[N];
  54. int query(int x, int y) {
  55. int ans = 0, lca;
  56. access(x), splay(x); ans += sum[x];
  57. lca = access(y), splay(y), ans += sum[y];
  58. access(lca), splay(lca), ans -= 2 * sum[lca];
  59. return ans;
  60. }
  61. int main() {
  62. int n, m, cnt = 1, last = 2, qs = 0;
  63. read(n), read(m);
  64. newnode(1); L[1] = 1, R[1] = n; id[1] = 1;
  65. newnode(0); link(2, 1);
  66. for (int i = 1; i <= m; i++) {
  67. int op; read(op);
  68. if (!op) {
  69. int l, r;
  70. read(l), read(r);
  71. newnode(1);
  72. L[++cnt] = l, R[cnt] = r, id[cnt] = tot;
  73. q[++len] = (node) {1, i - m, tot, last};
  74. }
  75. else if (op == 1) {
  76. int l, r, x;
  77. read(l), read(r), read(x);
  78. l = max(l, L[x]), r = min(r, R[x]);
  79. if (l <= r) {
  80. newnode(0); link(tot, last);
  81. q[++len] = (node) {l, i - m, tot, id[x]};
  82. q[++len] = (node) {r + 1, i - m, tot, last};
  83. last = tot;
  84. }
  85. }
  86. else {
  87. int x, u, v;
  88. read(x), read(u), read(v);
  89. q[++len] = (node) {x, ++qs, id[u], id[v]};
  90. }
  91. }
  92. sort(q + 1, q + len + 1);
  93. int j = 1;
  94. for (int i = 1; i <= n; i++)
  95. while (i == q[j].pos && j <= len) {
  96. if (q[j].op <= 0) cut(q[j].x), link(q[j].x, q[j].y);
  97. else ans[q[j].op] = query(q[j].x, q[j].y);
  98. j++;
  99. }
  100. for (int i = 1; i <= qs; i++) printf("%d\n", ans[i]);
  101. return 0;
  102. }

P3348 [ZJOI2016]大森林(Link-cut-tree)的更多相关文章

  1. [BJOI2014]大融合(Link Cut Tree)

    [BJOI2014]大融合(Link Cut Tree) 题面 给出一棵树,动态加边,动态查询通过每条边的简单路径数量. 分析 通过每条边的简单路径数量显然等于边两侧节点x,y子树大小的乘积. 我们知 ...

  2. P3348 [ZJOI2016]大森林

    \(\color{#0066ff}{ 题目描述 }\) 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点, ...

  3. ●洛谷P3348 [ZJOI2016]大森林

    题链: https://www.luogu.org/problemnew/show/P3348 题解: LCT,神题 首先有这么一个结论: 每次的1操作(改变生长点操作),一定只会会对连续的一段区间产 ...

  4. 洛谷P3348 [ZJOI2016]大森林 [LCT]

    传送门 刷了那么久水题之后终于有一题可以来写写博客了. 但是这题太神仙了我还没完全弄懂-- upd:写完博客之后似乎懂了. 思路 首先很容易想到\(O(n^2\log n)\)乘上\(O(\frac{ ...

  5. [NOI2014] 魔法森林 - Link Cut Tree

    [NOI2014] 魔法森林 Description 给定一张图,每条边 \(i\) 的权为 \((a_i,b_i)\), 求一条 \(1 \sim n\) 路径,最小化 \(\max_{i\in P ...

  6. P3348 [ZJOI2016]大森林(LCT)

    Luogu3348 BZOJ4573 LOJ2092 题解 对于每个\(1\)操作建一个虚点,以后的\(0\)操作都连在最近建好的虚点上.这样每次整体嫁接的时候,直接把这个虚点断掉它原来的父亲,再\( ...

  7. 洛谷P3348 [ZJOI2016]大森林(LCT,虚点,树上差分)

    洛谷题目传送门 思路分析 最简单粗暴的想法,肯定是大力LCT,每个树都来一遍link之类的操作啦(T飞就不说了) 考虑如何优化算法.如果没有1操作,肯定每个树都长一样.有了1操作,就来仔细分析一下对不 ...

  8. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  9. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

随机推荐

  1. cakephp执行原生sql语句

    $sql = 'select sum(amount) as amount from option_capital where status = 2 and amount > 0 and user ...

  2. VMWare 虚拟机挂载 Homestead NFS 进行老项目(基于 Brophp)维护

    环境: Laravel/homestead + winnfsd VMWare workstation 背景: 众所周知, windows 上成功配置 Homestead 进行开发时,为了解决文件系统的 ...

  3. ESXi系统命令行下启动虚拟机

    从命令行启动虚拟机: 用命令列出虚拟机的ID:vim-cmd vmsvc/getallvms |grep <vm name>注意: 第一列输出是vmid. 用命令查看虚拟机启动状态:vim ...

  4. Zookeeper 源码(三)Zookeeper 客户端源码

    Zookeeper 源码(三)Zookeeper 客户端源码 Zookeeper 客户端主要有以下几个重要的组件.客户端会话创建可以分为三个阶段:一是初始化阶段.二是会话创建阶段.三是响应处理阶段. ...

  5. 服务器上如何将D盘修改为E盘

    1.计算机管理→磁盘管理 2.右键点击需要调整的磁盘→更改驱动器号和路径 3.在弹出的设置框中→更改 4.点击右边的下拉箭头▼→选择一个盘符→确定 注意:如果盘符混乱,需要理顺,因为有些盘符占有了,不 ...

  6. int *a[] 与(int *)a【5】的区别

    *a[5] 是指针数组  可以指向5个值 (*a)[5]  是一个指针,但这个指针只能指向包含5个元素的一维数组 a是一个数组,每个元素都是个指针. b是一个指针,指向一个数组 1.int *a[5] ...

  7. solr分词一:mmseg4j

    刚接触Lucene2.x和Solr2.x的时候,谈到中文分词,会让我立即想到用庖丁中文分词,庖丁中文分词因巨大的中文词库以及支持不限制个数的用户自定义词库,而且是纯文本格式,一行一词,使用后台线程检测 ...

  8. c# 调用微信小程序

    //微信也不给个c#调用的例子 只好自己造咯:ps:大佬勿喷 1 public string GetWx(string code, string iv, string encryptedData) { ...

  9. C#基础笔记(第十七天)

    1.复习 ref 传地址 用的是同一块内存 一个改变另一个也随着改变 return n1 > n2 ? n1 : n2; 三元表达式 int max=GetMax(1,2,3,4,5,6,); ...

  10. 如何关闭SQL进程

    --通过下面的查询得到trace ID select * from sys.traces --修改下面的@traceid参数,关闭,删除对应的trace exec sp_trace_setstatus ...