\(\color{#0066ff}{ 题目描述 }\)

小Y家里有一个大森林,里面有n棵树,编号从1到n。一开始这些树都只是树苗,只有一个节点,标号为1。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。

小Y掌握了一种魔法,能让第l棵树到第r棵树的生长节点长出一个子节点。同时她还能修改第l棵树到第r棵树的生长节点。她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?

\(\color{#0066ff}{输入格式}\)

第一行包含 2 个正整数 n,m,共有 n 棵树和 m 个操作。

接下来 m 行,每行包含若干非负整数表示一个操作,操作格式为:

0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号加 1(例如,第一个 0 号操作产生的子节点标号为 2), l 到 r 之间的树长出的节点标号都相同。保证 1<=l<=r<=n 。

1 l r x 表示将第 l 棵树到第 r 棵树的生长节点改到标号为 x 的节点。对于 i (l<=i<=r)这棵树,如果标号 x的点不在其中,那么这个操作对该树不产生影响。保证 1<=l<=r<=n , x 不超过当前所有树中节点最大的标号。

2 x u v 询问第 x 棵树中节点 u 到节点 v 点的距离,也就是在第 x 棵树中从节点 u 和节点 v 的最短路上边的数量。保证1<=x<=n,这棵树中节点 u 和节点 v 存在。N<=\(10^5\),M<=\(2*10^5\)

\(\color{#0066ff}{输出格式}\)

输出包括若干行,按顺序对于每个小Y的询问输出答案

\(\color{#0066ff}{输入样例}\)

5 5
0 1 5
1 2 4 2
0 1 4
2 1 1 3
2 2 1 3

\(\color{#0066ff}{输出样例}\)

1
2

\(\color{#0066ff}{数据范围与提示}\)

none

\(\color{#0066ff}{ 题解 }\)

显然一个及其暴力的方法,LCT每次区间暴力link,绝对是T到飞起qwq

对于一个操作1,l,r,x,我们考虑它发生的变化。

假设改变后l-1的生长节点为a,l的生长节点为b

那么实际上就是之后a的所有子树都转接到b上

r和r+1同理,相当于又转接回来了

题目并没有强制在线,我们有只需记录两个端点,所以如果可以快速实现两个树的转移,就可以保证复杂度了

现在要解决的问题是,怎么才能改变一大堆子树的父亲

很重要的思想:建立虚点!

对于每一个1操作,建立一个虚点,在此之后的0操作都连在这个虚点上

这样每次在转接的时候,直接断虚点就行了

对于询问,我们需要设立一个点权

虚点点权为0,实点为1,实际上就是siz大小

但是。。。这个树有根啊,而且makeroot会改变父子关系,肯定是不行的

所以。。树上差分喽

直接\(s[x]+s[y]-s[lca]*2\)即可,这个LCA可以直接用LCT求

不难发现,对于询问,在把所有操作搞完之后再询问是不会有任何影响的

所以把当前位置都改完,之后再询问即可,(离线排个序)

#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 3e5 + 10;
struct node {
node *fa, *ch[2];
int siz, val;
node(int siz = 0, int val = 0): siz(siz), val(val) { ch[0] = ch[1] = fa = NULL; }
void upd() {
siz = val;
if(ch[0]) siz += ch[0]->siz;
if(ch[1]) siz += ch[1]->siz;
}
bool ntr() { return fa && (fa->ch[1] == this || fa->ch[0] == this); }
bool isr() { return fa->ch[1] == this; }
}pool[maxn];
struct que {
int pos, id, x, y;
que(int pos = 0, int id = 0, int x = 0, int y = 0): pos(pos), id(id), x(x), y(y) {}
friend bool operator < (const que &a, const que &b) {
return a.pos == b.pos? a.id < b.id : a.pos < b.pos;
}
}e[maxn];
void rot(node *x) {
node *y = x->fa, *z = y->fa;
bool k = x->isr(); node *w = x->ch[!k];
if(y->ntr()) z->ch[y->isr()] = x;
(x->ch[!k] = y)->ch[k] = w;
(y->fa = x)->fa = z;
if(w) w->fa = y;
y->upd(), x->upd();
}
void splay(node *o) {
while(o->ntr()) {
if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
rot(o);
}
}
node *access(node *x) {
node *lst = x;
for(node *y = NULL; x; x = (y = x)->fa)
splay(x), x->ch[1] = y, x->upd(), lst = x;
return lst;
}
void link(int l, int r) {
node *x = pool + l, *y = pool + r;
splay(x), x->fa = y;
}
void cut(int l) {
node *o = pool + l;
access(o), splay(o);
o->ch[0] = o->ch[0]->fa = NULL;
o->upd();
}
int n, m, at[maxn], atl[maxn], atr[maxn], qnum, ans[maxn];
int main() {
n = in(), m = in();
int real = 1, now = 2, xu = 2, num = 0; //实际节点编号,所有节点编号,当前虚点,操作
pool[1] = node(1, 1);
at[1] = atl[1] = 1, atr[1] = n; //at[i]代表实际编号为i的点在LCT的编号
link(xu, 1); //先连个虚点(初始的生长节点是1)
int p, l, r, x;
for(int i = 1; i <= m; i++) {
p = in(), l = in(), r = in();
if(p == 0) {
pool[++now] = node(1, 1); //开新节点
link(at[++real] = now, xu); //连上虚点
atl[real] = l, atr[real] = r; //atl和atr记录实际节点出现在树的区间范围
}
if(p == 1) {
x = in();
l = std::max(l, atl[x]), r = std::min(r, atr[x]); //区间求交,找到x出现的位置
if(l > r) continue; //x没有出现。。
link(++now, xu); //建立虚点
e[++qnum] = que(l, i - m, now, at[x]); //两端点信息
e[++qnum] = que(r + 1, i - m, now, xu); //id为负,这样就能先操作后询问
xu = now; //更新当前虚点
}
if(p == 2) x = in(), e[++qnum] = que(l, ++num, at[r], at[x]);
}
std::sort(e + 1, e + qnum + 1);
for(int i = 1; i <= qnum; i++) {
if(e[i].id > 0) {
node *x = pool + e[i].x, *y = pool + e[i].y;
access(x), splay(x), ans[e[i].id] += x->siz;
node *lca = access(y); splay(y); ans[e[i].id] += y->siz;
access(lca), splay(lca), ans[e[i].id] -= (lca->siz << 1);
}
else cut(e[i].x), link(e[i].x, e[i].y);
}
for(int i = 1; i <= num; i++) printf("%d\n", ans[i]);
return 0;
}

P3348 [ZJOI2016]大森林的更多相关文章

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

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

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

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

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

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

  4. P3348 [ZJOI2016]大森林(Link-cut-tree)

    传送门 题解 题面大意: \(0.\)区间加节点 \(1.\)区间换根 \(2.\)单点询问距离 如果没有\(1\)操作,因为区间加节点都是加在下面,所以我们可以直接把\(n\)棵树压成一棵树,直接询 ...

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

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

  6. BZOJ4573:[ZJOI2016]大森林——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=4573 https://www.luogu.org/problemnew/show/P3348#sub ...

  7. [ZJOI2016]大森林(LCT)

    题目描述 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力. 小Y掌握了一种 ...

  8. [ZJOI2016]大森林

    Description: 小Y家里有一个大森林,里面有n棵树,编号从1到n 0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号加 1(例 ...

  9. 【刷题】BZOJ 4573 [Zjoi2016]大森林

    Description 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力.小 ...

随机推荐

  1. C# Lambda快速深度拷贝

    背景:今天上班在班车上和一个同事讨论有关C#拷贝效率的问题,聊到了多种深度拷贝方法,其中就提到了一种Lambda表达式拷贝的方法,这位同事说这种深度拷贝快是快但是如果对象里面再嵌入对象就不能深度拷贝了 ...

  2. java--构造器与static

    原本无显示编码构造器,则有一个默认的隐式(隐藏的无参构造器),但是,当显示指定了构造器,则这个默认隐式的构造器将不存在,比如此时无法new无参的构造器(除非显示地编写声明无参的构造函数). 如果子类构 ...

  3. Python函数(五)-高阶函数

    函数接收的参数可以是数字,字符串,列表,元组,字典,集合,也可以是另一个函数,那么这个接收一个函数作为参数的函数就称为高阶函数 # -*- coding:utf-8 -*- __author__ = ...

  4. angular之增删改查

    <!DOCTYPE html> <html lang="en" ng-app="app"> <head> <meta ...

  5. python window使用paramiko简单监控数据指标数据采集

    #!/usr/bin/python #-*- coding: utf-8 -*- #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ...

  6. ffmpeg: ‘UINT64_C’ was not declared in this scope (转)

    ffmpeg 默认是用C文件来编译的,如果某个CPP文件想引用ffmpeg中的某些函数或者头文件,有可能出现 ‘UINT64_C’ was not declared in this scope的错误 ...

  7. myeclipse.ini

    myeclipse10 32位 我的配置 #utf8 (do not remove) #utf8 (do not remove) -startup ../Common/plugins/org.ecli ...

  8. POJ 1191 棋盘分割 (区间DP,记忆化搜索)

    题面 思路:分析公式,我们可以发现平均值那一项和我们怎么分的具体方案无关,影响答案的是每个矩阵的矩阵和的平方,由于数据很小,我们可以预处理出每个矩阵的和的平方,执行状态转移. 设dp[l1][r1][ ...

  9. [转]AJAX工作原理及其优缺点

    1.什么是AJAX?AJAX全称为“Asynchronous JavaScript and XML”(异步JavaScript和XML),是一种创建交互式网页应用的网页开发技术.它使用:使用XHTML ...

  10. php学习笔记-if else

    生活不会总是波澜不惊的,要是那样也没有什么意思.代码也一样,if else的出现为平静的生活带来一点变化. <?php $money = 1000; if($money>0) { //sl ...