传送门

题解

题面大意:

\(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

#include<bits/stdc++.h>

#define LL long long
#define RG register using namespace std;
template<class T> inline void read(T &x) {
x = 0; RG char c = getchar(); bool f = 0;
while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
x = f ? -x : x;
return ;
}
template<class T> inline void write(T x) {
if (!x) {putchar(48);return ;}
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 500010;
int ch[N][2], val[N], sum[N], fa[N], tot;
bool isroot(int x) { return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }
#define get(x) (ch[fa[x]][1] == x)
void pushup(int x) { sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x]; }
void rotate(int x) {
int y = fa[x], z = fa[y], k = get(x);
if (!isroot(y)) ch[z][get(y)] = x; fa[x] = z;
ch[y][k] = ch[x][k ^ 1]; fa[ch[x][k ^ 1]] = y;
ch[x][k ^ 1] = y; fa[y] = x;
pushup(y);
}
void splay(int x) {
while (!isroot(x)) {
int y = fa[x];
if (!isroot(y))
(get(x) ^ get(y)) ? rotate(x) : rotate(y);
rotate(x);
}
pushup(x);
}
int access(int x) {
int y = 0; for (; x; y = x, x = fa[x]) splay(x), ch[x][1] = y, pushup(x);
return y;
}
void link(int x, int y) { access(x); splay(x); fa[x] = y; }
void cut(int x) { access(x); splay(x); ch[x][0] = fa[ch[x][0]] = 0; pushup(x); }
void newnode(int x) { val[++tot] = x; sum[tot] = x; }
int L[N], R[N], id[N], len;
struct node {
int pos, op, x, y;
bool operator < (const node &z) const {
return pos == z.pos ? op < z.op : pos < z.pos;
}
}q[N];
int ans[N];
int query(int x, int y) {
int ans = 0, lca;
access(x), splay(x); ans += sum[x];
lca = access(y), splay(y), ans += sum[y];
access(lca), splay(lca), ans -= 2 * sum[lca];
return ans;
}
int main() {
int n, m, cnt = 1, last = 2, qs = 0;
read(n), read(m);
newnode(1); L[1] = 1, R[1] = n; id[1] = 1;
newnode(0); link(2, 1);
for (int i = 1; i <= m; i++) {
int op; read(op);
if (!op) {
int l, r;
read(l), read(r);
newnode(1);
L[++cnt] = l, R[cnt] = r, id[cnt] = tot;
q[++len] = (node) {1, i - m, tot, last};
}
else if (op == 1) {
int l, r, x;
read(l), read(r), read(x);
l = max(l, L[x]), r = min(r, R[x]);
if (l <= r) {
newnode(0); link(tot, last);
q[++len] = (node) {l, i - m, tot, id[x]};
q[++len] = (node) {r + 1, i - m, tot, last};
last = tot;
}
}
else {
int x, u, v;
read(x), read(u), read(v);
q[++len] = (node) {x, ++qs, id[u], id[v]};
}
}
sort(q + 1, q + len + 1);
int j = 1;
for (int i = 1; i <= n; i++)
while (i == q[j].pos && j <= len) {
if (q[j].op <= 0) cut(q[j].x), link(q[j].x, q[j].y);
else ans[q[j].op] = query(q[j].x, q[j].y);
j++;
}
for (int i = 1; i <= qs; i++) printf("%d\n", ans[i]);
return 0;
}

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. 如何使用vsphere client 克隆虚拟机

    vSphere 是VMware公司推出一套服务器虚拟化解决方案. 工具/原料 vSphere 测试系统 方法/步骤 1.进入vSphere client,关闭需要克隆的虚拟机win7 2.选中ESXi ...

  2. PHP语言性能优化——少使用魔术方法

    对以下使用魔术方法和不适用魔术方法运行时间进行比较 使用魔术方法test1.php: <?php /** * 测试类 */ class test { private $name = " ...

  3. 禁止进入activity自动弹出键盘

    禁止进入activity自动弹出键盘 在Manifest.xml中设定activity的属性 android:windowSoftInputMode="stateHidden|stateUn ...

  4. 关于自动ui的多个处理我的解决方案, 不知道大家怎么处理

    ( pathpp ="D:\\pyc-tools\\ms\\newdate\\maxinfo" DialogMonitorOPS.unRegisterNotification id ...

  5. SqlServer循环执行存储过程

    begin --申明变量 ) declare @zycs int --赋值变量 --申明游标 declare order_cursor cursor for (select blh, zycs fro ...

  6. leetcode 实现strStr()

    实现strStr()函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返回 ...

  7. 在VC++中执行VBS代码

    此代码来自https://blog.csdn.net/zhu2695/article/details/13770671 作者: zhu2695   时间:2013年10月31日 13:08:41 #i ...

  8. squid代理缓存服务

    man.linuxde.net 1.squid是Linux系统中的代理缓存服务,通常用作WEB网站的前置缓存服务,能够代替用户向网站服务器请求页面数据并进行缓存. 2.squid服务特点:配置简单.效 ...

  9. Java : java.util.ConcurrentModificationException

    在删除 List 元素的时候,要用 Iterator,不要直接遍历 List,否则会出现 Fatal Exception: java.util.ConcurrentModificationExcept ...

  10. kubectl get componentstatus ERROR:HTTP probe failed with statuscode: 503

    通过kubectl命令可以查看k8s各组件的状态: [root@wecloud-test-k8s-1 ~]# kubectl get cs NAME STATUS MESSAGE ERROR cont ...