题目传送门

Description

现在有 \(n\) 棵以 \(1\) 为根的树,每棵树有一个生长节点,有 \(m\) 次操作,每次操作是下面三种中的一个:

  • 在 \(l\sim r\) 的这些树的生长节点下面增加一个新的节点

  • 将 \(l\sim r\) 的生长节点都变为 \(x\)

  • 查询第 \(x\) 棵树种 \(u\sim v\) 的距离

\(n\le 10^5,m\le 2\times 10^5\)

Solution

我们可以发现的是,我们加点或者增加新节点并不会改变已有查询的答案,也就是说,我们是可以离线的。另外,我们可以发现,增加多的点并不会有什么影响,也就是说,我们第一个操作就没有什么意义了,可以大家一起加。

考虑操作 \(2\),可以想到的是,假如我们的生长节点是 \(x_1\to x_2\),那么,其实我们也可以理解为生长节点不变,在查询的时候,把更改后这段时间中增加的节点都加到 \(x_2\) 来。于是,我们就需要实现类似于子树转移的东西,这个可以新建一个虚点,所有点都连到虚点上,直接虚点改变父亲就好了。

对于查询而言,我们肯定是离线下来。因为每次操作都是区间操作,所以我们可以用类似于差分的方法来一棵一颗的统计。我们可以先处理出来每次要在哪里把那个子树转移到哪里。对于操作 \(2\) 来说,就可以变为在 \(l\) 处把上一次操作 \(2\) 到此次操作 \(2\) 之间加入的点的子树都转移到 \(x_2\),然后在 \(r+1\) 又转移到 \(x_1\)。

于是,问题就是如何查询一棵树上两个点之间的距离了。我们可以直接用 \(\text{access}\) 求到两个点的 \(\text{lca}\) 然后差分一波就好了。

时间复杂度 \(\Theta((n+m)\log n)\)。

Code

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define MAXN 300005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int n,m,cnt,fuc,las,tot,qL[MAXN],qR[MAXN],ans[MAXN],ind[MAXN],sum[MAXN],val[MAXN],par[MAXN],son[MAXN][2]; bool rnk (int x){return son[par[x]][1] == x;}
bool Isroot (int x){return son[par[x]][0] != x && son[par[x]][1] != x;}
void Pushup (int x){sum[x] = sum[son[x][0]] + sum[son[x][1]] + val[x];}
void newnode (int v){++ cnt,val[cnt] = sum[cnt] = v;}
void rotate (int x){
int y = par[x],z = par[y],k = rnk (x),w = son[x][!k];
if (!Isroot (y)) son[z][rnk (y)] = x;son[x][!k] = y,son[y][rnk (x)] = w;
if (w) par[w] = y;par[x] = z,par[y] = x;
Pushup (y),Pushup (x);
}
void Splay (int x){
while (!Isroot (x)){
int y = par[x];
if (!Isroot (y)) rotate (rnk (x) == rnk (y) ? y : x);
rotate (x);
}
}
int Access (int x){int y;for (y = 0;x;x = par[y = x]) Splay (x),son[x][1] = y,Pushup (x);return y; }
int getans (int x,int y){
int ans = 0,s;Access (x),Splay (x),ans += sum[x],s = Access (y),Splay (y),ans += sum[y],Access (s),Splay (s),ans -= 2 * sum[s];
return ans;
}
void link (int x,int y){Splay (x),par[x] = y;}
void cut (int x){Access (x),Splay (x),par[son[x][0]] = 0,son[x][0] = 0;Pushup (x);} struct node{
int pos,tim,x,y;
bool operator < (const node &p)const{return pos != p.pos ? pos < p.pos : tim < p.tim;}
}q[MAXN]; signed main(){
read (n,m);newnode (1),qL[1] = 1,qR[1] = n,las = ind[fuc = 1] = 1;int qn = 0;
for (Int i = 1;i <= m;++ i){
int opt,x,y;read (opt,x,y);
if (opt == 0) ++ fuc,newnode (1),ind[fuc] = cnt,qL[fuc] = x,qR[fuc] = y,q[++ tot] = node {1,i - m,cnt,las};
else if (opt == 1){
int k;read (k);
x = max (x,qL[k]),y = min (y,qR[k]);
if (x <= y) newnode (0),link (cnt,las),q[++ tot] = node {x,i - m,cnt,ind[k]},q[++ tot] = node {y + 1,i - m,cnt,las},las = cnt;
}
else{
int k;read (k);
q[++ tot] = node {x,++ qn,ind[y],ind[k]};
}
}
sort (q + 1,q + tot + 1),memset (ans,-1,sizeof (ans));
for (Int i = 1,j = 1;i <= n;++ i)
for (;q[j].pos == i;++ j){
if (q[j].tim <= 0) cut (q[j].x),link (q[j].x,q[j].y);
else ans[q[j].tim] = getans (q[j].x,q[j].y);
}
for (Int i = 1;i <= qn;++ i) write (ans[i]),putchar ('\n');
return 0;
}

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

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

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

  2. [ZJOI2016]大森林(LCT)

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

  3. bzoj 4573: [Zjoi2016]大森林

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

  4. P3348 [ZJOI2016]大森林

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

  5. [ZJOI2016]大森林

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

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

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

  7. 【LuoguP3348】[ZJOI2016]大森林

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

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

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

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

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

随机推荐

  1. APP 兼容性测试之云测平台体验

    前言 兼容性测试主要通过人工或自动化的方式,在需要覆盖的终端设备上进行功能用例执行,查看软件性能.稳定性等是否正常. 对于需要覆盖的终端设备,大型互联网公司,像BAT,基本都有自己的测试实验室,拥有大 ...

  2. JSTL标签报错-http://java.sun.com/jsp/jstl/core

    考虑为tomcat缺少相关的包 导入就好了 导入jstl-api-1.2.jar 以及standard-1.1.2.jar 然后重启服务 更多java学习,请进本人小博客-https://zhangj ...

  3. Python3实现打格点算法的GPU加速

    技术背景 在数学和物理学领域,总是充满了各种连续的函数模型.而当我们用现代计算机的技术去处理这些问题的时候,事实上是无法直接处理连续模型的,绝大多数的情况下都要转化成一个离散的模型再进行数值的计算.比 ...

  4. centos7安装privoxy

    本文分为三部分,第一部分是在阿里云的ECS上安装Privoxy,第二部分是在AWS的EC2上安装Privoxy,第三部分是Privoxy的配置. 第一部分:阿里云ECS安装Privoxy 配置yum源 ...

  5. Linux proc文件系统小记

    序言: 当linux系统存在多个网口时(也可以是一个网口配置两个IP),每个网口配置不同的IP地址,并且分别连接到不同的电脑上,电脑分别配置与相连的网口统一网段的IP且将电脑网关设置为linux的网口 ...

  6. 10分钟学会VS NuGet包私有化部署

    前言 我们之前实现了打包发布NuGet,但是发布后的引用是公有的,谁都可以访问,显然这种方式是不可取的. 命令版本:10分钟学会Visual Studio将自己创建的类库打包到NuGet进行引用(ne ...

  7. JDK7&JDK9处理异常新特性

    1.JDK7新特性是在 try (定义对象,作用域就是try方法体) 复制一个文件实例: 复制文件的原理: 先从硬盘写出到内存中,创建文件输入流对象 FileInputStream fis; 中间是在 ...

  8. vue+element+echarts饼状图+可折叠列表

    html: <div id="echartsDiv" style="width: 48%; height: 430px; float: left;"> ...

  9. python库--pandas--Series

    方法 返回数据类型 参数 说明 Series(一维)       .Series() Series 实例s 创建一维数据类型Series data=None 要转化为Series的数据(也可用dict ...

  10. Spring Boot 2.x 之 Logging

    [源起] 最近在看Apollo的源码,发现其all-in-one项目的脚本demo.sh在执行的时候,竟然没有向控制台输出Spring Boot的日志. 我们修改后构建的Fat Jar,在启动时却打印 ...