传送门

题解

题面大意:

\(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. using namespace cv

    在OpenCV中使用 using namespace cv 的作用: 在使用#include语句包含相应头文件后,使用下面语句即可包含相应的Opencv命名空间 using namespace cv; ...

  2. iOS6后的内存警告处理

    [iOS6后的内存警告处理] The memory used by a view to draw itself onscreen is potentially quite large. However ...

  3. 深入浅出谈数据挖掘zz

    编者的话:本文对数据挖掘概念的产生,数据挖掘与常规数据分析的主要区别,所能解决的几大类问题和所应用的领域都有着非常清晰的论述.作者在此篇文章中认为数据挖掘最重要的要素是分析人员的相关业务知识和思维模式 ...

  4. hadoop 分布式集群安装

    这一套环境搭完,你有可能碰到无数个意想不到的情况. 用了1周的时间,解决各种linux菜鸟级的问题,终于搭建好了.. 沿途的风景,甚是历练. 环境介绍: 系统:win7 内存:16G(最低4G,不然跑 ...

  5. Hadoop中Writable类之四

    1.定制Writable类型 Hadoop中有一套Writable实现,例如:IntWritable.Text等,但是,有时候可能并不能满足自己的需求,这个时候,就需要自己定制Writable类型. ...

  6. (深搜)Oil Deposits -- hdu -- 1241

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=1241 Time Limit: 2000/1000 MS (Java/Others)    Memory ...

  7. Jetty 源码分析

    一. 总括      你了解Jetty 吗,就像我们所熟知的Tomcat一样, Jetty是一个免费的开放源码的100%纯Java的Http服务器和Servlet容器. Jetty具备以下特点:   ...

  8. 【树状DP】星象仪

    题目描述 在寂寞的夜里,星象仪是非常浪漫的东西.但是,你作为一个精神稍微有点不太正常的Geek,把原本正常的星象仪改造得像电报发送器一样.当然,你这个的构造还要更加奇葩一点.具体来说,你的星象仪是一棵 ...

  9. Android-MediaRecorder录像机(视频)

    在上一篇博客,Android-MediaRecorder录制音频,中讲解了使用Android API MediaRecorder 刻录音频,这篇博客主要是介绍 使用MediaRecorder刻录(视频 ...

  10. ES6学习之let声明变量的学习

    1.let和var类似, (1)let与var不同的点:let没有预编译,变量提升这个过程,let声明的变量只能在当前作用域内访问到(一个{}可以看做是一个作用域),在全局var声明的变量属于wind ...