P3348 [ZJOI2016]大森林(Link-cut-tree)
题解
题面大意:
\(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)的更多相关文章
- [BJOI2014]大融合(Link Cut Tree)
[BJOI2014]大融合(Link Cut Tree) 题面 给出一棵树,动态加边,动态查询通过每条边的简单路径数量. 分析 通过每条边的简单路径数量显然等于边两侧节点x,y子树大小的乘积. 我们知 ...
- P3348 [ZJOI2016]大森林
\(\color{#0066ff}{ 题目描述 }\) 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点, ...
- ●洛谷P3348 [ZJOI2016]大森林
题链: https://www.luogu.org/problemnew/show/P3348 题解: LCT,神题 首先有这么一个结论: 每次的1操作(改变生长点操作),一定只会会对连续的一段区间产 ...
- 洛谷P3348 [ZJOI2016]大森林 [LCT]
传送门 刷了那么久水题之后终于有一题可以来写写博客了. 但是这题太神仙了我还没完全弄懂-- upd:写完博客之后似乎懂了. 思路 首先很容易想到\(O(n^2\log n)\)乘上\(O(\frac{ ...
- [NOI2014] 魔法森林 - Link Cut Tree
[NOI2014] 魔法森林 Description 给定一张图,每条边 \(i\) 的权为 \((a_i,b_i)\), 求一条 \(1 \sim n\) 路径,最小化 \(\max_{i\in P ...
- P3348 [ZJOI2016]大森林(LCT)
Luogu3348 BZOJ4573 LOJ2092 题解 对于每个\(1\)操作建一个虚点,以后的\(0\)操作都连在最近建好的虚点上.这样每次整体嫁接的时候,直接把这个虚点断掉它原来的父亲,再\( ...
- 洛谷P3348 [ZJOI2016]大森林(LCT,虚点,树上差分)
洛谷题目传送门 思路分析 最简单粗暴的想法,肯定是大力LCT,每个树都来一遍link之类的操作啦(T飞就不说了) 考虑如何优化算法.如果没有1操作,肯定每个树都长一样.有了1操作,就来仔细分析一下对不 ...
- LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...
- Link Cut Tree学习笔记
从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...
随机推荐
- centos7 dubbokeeper安装
下载dubbokeeper源码 git clone https://github.com/dubboclub/dubbokeeper mysql 先执行install-mysql.sh 编译好 ...
- SLAM拾萃(2):doxygen
今天给大家介绍一下doxygen.这个工具由来已久了,至少08年左右就已经在用了,但是目前还没见到好的介绍.我个人觉得这是个很简单易用的工具,但是为什么看了别人介绍反而觉得复杂了……所以趁着今天比较闲 ...
- spring 启动完成后事件监听器处理
有时候我们在spring容器启动完成后,我们需要做一些处理动作,这个时候怎么做呢? spring提供了事件监听器的处理机制. spring提供了内置的几类的事件: ContextClosedEvent ...
- HAService 刨坑
High availability is a characteristic of a system, which describes the duration (length of time) for ...
- windows7 不能更新,提示:"WindowsUpdate_80240016" "WindowsUpdate_dt000",如何解决?
计算机(右键) ---- 管理 -------- 服务和应用程序 -----服务(找到名称为windows update的服务,并且在windwos update服务右键 选择重新启动 ) 再次安装更 ...
- polymer入门例子-已过时
这个教程挺不错!:http://blog.csdn.net/renfufei/article/details/37040883 过时了,现在的版本已经为1.0了 一:创建APP结构 本教程会使用预先构 ...
- 前端与HTTP
本文整理在,我的github 上.欢迎Star. 各版本的http 发展 在HTTP建立之初,主要是为了传输超文本标记语言(HTML)文档.随着时代的发展,也进行了若干次演进.下图是各个版本发布的时间 ...
- HTML5、CSS3与响应式Web设计入门(1)
HTML5与CSS3已经当仁不让的成为了这两年Web界最火爆的词,他们似乎在HTML4和CSS2统治了Web很多年之后的某一天突然爆发,然 后一直占据着所有Web开发者的视野.HTML5本身就是一个很 ...
- C#Encoding
1.Encoding (1).如何生成一个Encoding即一种编码 Encoding位于System.Text命名空间下,是一个抽象类,它的派生类如下图: 要实例化一个Encoding一共有以下两种 ...
- django def validate_column和validate
VIewDemo class RegUserSet(mixins.CreateModelMixin,viewsets.GenericViewSet): serializer_class = RegUs ...