@description@

小 Y 家里有一个大森林,里面有 n 棵树,编号从 1 到 n。一开始这些树都只是树苗,只有一个节点,标号为 1。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。小 Y 掌握了一种魔法,能让第 l 棵树到第 r 棵树的生长节点长出一个子节点。同时她还能修改第 l 棵树到第 r 棵树的生长节点。她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?

输入格式

第一行包含两个正整数 ,表示共有 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 存在。

输出格式

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

样例输入

5 5

0 1 5

1 2 4 2

0 1 4

2 1 1 3

2 2 1 3

样例输出

1

2

数据范围与提示

N <= 10^5, M <= 2*10^5。

@solution@

对于每个 1 操作 l r x,我们发现它能影响到的树就是含有点 x 的树。而含有点 x 的树对应了一个 0 操作 l' r'。

则它能产生影响的树实际上只有 [l, r] 与 [l', r'] 的交集,可以发现这个也是一个区间。

因此,我们可以把 1 操作变成 “将某个区间内所有树的生长结点改成 x”,并且保证该区间都存在 x 这个结点。

然后我们再来看 0 操作。因为 1 操作(变化过后的)与 2 操作都保证了结点的存在性,那么假如我们加入一些不存在的结点,也不会影响答案。

因此 0 操作给定的区间 [l, r] 就没有什么用了。我们就可以视作所有树都长了这样一个结点出来。

对于 2 操作,我们的解决方法是:先维护出这棵树长什么样子,然后查询。

我们可以将 n 棵树看成 1 棵树的 n 个时刻,则每个 1 操作相当于在第 l 个时刻初插入一个操作,在第 r 个时刻末删除一个操作。

然后,对于每个 1 操作我们都建立 1 个虚点,第 i 个虚点连向第 i 次 1 操作到第 i+1 次 1 操作之间的 0 操作加入的实点(因为这些点在任意时刻都是在同一个父亲下的)。

假如第 i 个虚点连向第 j 个虚点,则我们理解为第 i 个虚点所管的实点与第 j 个虚点所管的实点在同一个父亲下。

假如第 i 个虚点连向一个实点 x,则我们理解为第 i 个虚点所管的实点的父亲为 x。

初始时所有操作都未执行,那么每个 1 操作对应的虚点向前一个虚点连边;第一个虚点连向结点 1。

每次插入一个 1 操作,就把这个 1 操作对应的虚点 i 连向实点 x;每次删除时,把这个 1 操作对应的虚点 i 连回第 i-1 个虚点。

查询不能直接查询两点之间的路径上的实点数量,需要对 lca 是实点还是虚点进行分类。

不过有一个替代方案:记 f(x) 表示 x 到根的实点数量,则直接查 f(u) + f(v) - 2*f( lca(u, v) ) 即可。

至于为什么,还是需要对 lca 是实点还是虚点进行分类。

@accepted code@

#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int MAXM = 200000;
struct link_cut_tree{
struct node{
int rev;
int siz, key;
node *ch[2], *fa;
}pl[MAXM + 5], *ncnt, *NIL;
link_cut_tree() {
ncnt = NIL = &pl[0];
NIL->fa = NIL->ch[0] = NIL->ch[1] = NIL;
NIL->siz = NIL->key = 0;
}
void maintain(node *x) {
if( x != NIL ) {
swap(x->ch[0], x->ch[1]);
x->rev ^= 1;
}
}
void pushup(node *x) {x->siz = x->ch[0]->siz + x->ch[1]->siz + x->key;}
void pushdown(node *x) {
if( x->rev )
maintain(x->ch[0]), maintain(x->ch[1]), x->rev = 0;
}
node *newnode(int k) {
node *p = (++ncnt);
p->fa = p->ch[0] = p->ch[1] = NIL;
p->siz = p->key = k;
return p;
}
bool is_root(node *x) {
return x->fa->ch[0] != x && x->fa->ch[1] != x;
}
void set_child(node *x, node *y, int d) {
if( x != NIL ) x->ch[d] = y;
if( y != NIL ) y->fa = x;
}
void rotate(node *x) {
node *y = x->fa; int d = (y->ch[1] == x);
pushdown(y), pushdown(x);
if( is_root(y) ) x->fa = y->fa;
else set_child(y->fa, x, y->fa->ch[1] == y);
set_child(y, x->ch[!d], d);
set_child(x, y, !d);
pushup(y);
}
void splay(node *x) {
pushdown(x);
while( !is_root(x) ) {
node *y = x->fa;
if( is_root(y) )
rotate(x);
else {
node *z = y->fa;
if( (z->ch[1] == y) == (y->ch[1] == x) )
rotate(y);
else rotate(x);
rotate(x);
}
}
pushup(x);
}
void access(node *x) {
node *y = NIL;
while( x != NIL ) {
splay(x);
x->ch[1] = y;
pushup(x);
y = x, x = x->fa;
}
}
void make_root(node *x) {
access(x), splay(x), maintain(x);
}
void link(node *x, node *y) {
make_root(x), x->fa = y;
}
void cut(node *x, node *y) {
make_root(x), access(y), splay(y);
x->fa = NIL, y->ch[0] = y->ch[1] = NIL;
pushup(y);
}
node *lca(node *x, node *y) {
access(x), access(y), splay(x);
if( x->fa == NIL ) return x;
else return x->fa;
}
int query(node *x, node *y) {
access(x), splay(x); int p = x->siz;
access(y), splay(y); int q = y->siz;
node *l = lca(x, y);
access(l), splay(l); int r = l->siz;
return p + q - 2*r;
}
}T;
struct modify{link_cut_tree::node *a, *b, *c;}tmp;
struct node{int id, u, v;}tmp2;
vector<modify>mdf[MAXM + 5];
vector<node>qry[MAXM + 5];
link_cut_tree::node *nd0[MAXM + 5], *nd1[MAXM + 5];
int le[MAXM + 5], ri[MAXM + 5];
int cnt0, cnt1, cnt2;
int ans[MAXM + 5];
int main() {
int n, m; scanf("%d%d", &n, &m);
nd0[cnt0 = 1] = nd1[cnt1 = 1] = T.newnode(0);
le[1] = 1, ri[1] = n;
for(int i=1;i<=m;i++) {
int op; scanf("%d", &op);
if( op == 0 ) {
cnt0++; scanf("%d%d", &le[cnt0], &ri[cnt0]);
nd0[cnt0] = T.newnode(1);
T.link(nd0[cnt0], nd1[cnt1]);
}
else if( op == 1 ) {
int l, r, x; scanf("%d%d%d", &l, &r, &x);
l = max(l, le[x]), r = min(r, ri[x]);
if( l > r ) continue;
nd1[++cnt1] = T.newnode(0);
tmp.a = nd1[cnt1], tmp.b = nd1[cnt1-1], tmp.c = nd0[x], mdf[l].push_back(tmp);
tmp.a = nd1[cnt1], tmp.b = nd0[x], tmp.c = nd1[cnt1-1], mdf[r + 1].push_back(tmp);
T.link(nd1[cnt1], nd1[cnt1-1]);
}
else {
int x, u, v; scanf("%d%d%d", &x, &u, &v);
tmp2.id = (++cnt2), tmp2.u = u, tmp2.v = v, qry[x].push_back(tmp2);
}
}
for(int i=1;i<=n;i++) {
for(int j=0;j<mdf[i].size();j++)
T.cut(mdf[i][j].a, mdf[i][j].b), T.link(mdf[i][j].a, mdf[i][j].c);
for(int j=0;j<qry[i].size();j++)
T.make_root(nd0[1]), ans[qry[i][j].id] = T.query(nd0[qry[i][j].u], nd0[qry[i][j].v]);
}
for(int i=1;i<=cnt2;i++)
printf("%d\n", ans[i]);
}

@details@

ZJOI 的题都是神仙题 * 3。

实现起来倒是很顺畅,基本上都是模板。主要是理解。

@loj - 2092@ 「ZJOI2016」大森林的更多相关文章

  1. 「ZJOI2016」大森林 解题报告

    「ZJOI2016」大森林 神仙题... 很显然线段树搞不了 考虑离线操作 我们只搞一颗树,从位置1一直往后移动,然后维护它的形态试试 显然操作0,1都可以拆成差分的形式,就是加入和删除 因为保证了操 ...

  2. loj2092 「ZJOI2016」大森林

    ref不是太懂-- #include <algorithm> #include <iostream> #include <cstring> #include < ...

  3. LOJ#2230. 「BJOI2014」大融合

    LOJ#2230. 「BJOI2014」大融合 题目描述 小强要在$N$个孤立的星球上建立起一套通信系统.这套通信系统就是连接$N$个点的一个树.这个树的边是一条一条添加上去的. 在某个时刻,一条边的 ...

  4. @loj - 2090@ 「ZJOI2016」旅行者

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 小 Y 来到了一个新的城市旅行.她发现了这个城市的布局是网格状的 ...

  5. Loj 2230. 「BJOI2014」大融合 (LCT 维护子树信息)

    链接:https://loj.ac/problem/2230 思路: 设立siz数组保存虚点信息,sum表示总信息 维护子树信息link操作和access操作需要进行一些改动 可参考博客:https: ...

  6. @loj - 2093@ 「ZJOI2016」线段树

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 小 Yuuka 遇到了一个题目:有一个序列 a1,a2,..., ...

  7. @loj - 2091@ 「ZJOI2016」小星星

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 小 Y 是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有 ...

  8. 「ZJOI2016」解题报告

    「ZJOI2016」解题报告 我大浙的省选题真是超级神仙--这套已经算是比较可做的了. 「ZJOI2016」旅行者 神仙分治题. 对于一个矩形,每次我们从最长边切开,最短边不会超过 \(\sqrt{n ...

  9. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

随机推荐

  1. NSIS之MUI

    NSIS 2.0 版本支持定制的用户界面.所谓的 Modern UI(下称 MUI) 就是一种模仿最新的 Windows 界面风格的界面系统.MUI 改变了 NSIS 脚本的编写习惯,它使用 NSIS ...

  2. Spring Bean 作用域

    Bean 的作用域 当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项.例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean ...

  3. Redis分布式锁的实现及注意事项

    一.前言 分布式锁一般有三种实现方式: 1. 数据库乐观锁: 2. 基于Redis的分布式锁: 3. 基于ZooKeeper的分布式锁. 本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上 ...

  4. arcgis点密度专题

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  5. Celery-一个会做异步任务,定时任务的芹菜

    Celery 分布式任务队列 同步与异步 比如说你要去一个餐厅吃饭,你点完菜以后假设服务员告诉你,你点的菜,要两个小时才能做完,这个时候你可以有两个选择 一直在餐厅等着饭菜上桌 你可以回家等着,这个时 ...

  6. 去掉CSS赘余代码,CSS可以更简洁

    本篇文章适合css新手学习,对于已经掌握了css的朋友们也可以通过本片文章来复习知识. 作者通过实践,认为在有些情况下css的代码是可以更加简洁的,多数情况下是因为新手对于一些具有多属性的元素代码不能 ...

  7. Quarz框架学习

    参考博客:https://www.cnblogs.com/zhanghaoliang/p/7886110.html

  8. laravel学习文档

    https://github.com/barryvdh/laravel-debugbar Laravel 精选资源大全 http://laravelacademy.org/post/153.html ...

  9. AnalyticDB for MySQL 3.0 技术架构解析

    企业数据需求不断变化,近年来变化趋势日益明显,从数据的3V特性看:体积,速度和变化:Big Data强调数据量,PB级以上,是静态数据.而Fast Data在数据量的基础上,意味着速度和和变化,意味着 ...

  10. windows下 python中报错ImportError: No module named 'requests'

    原因没有安装requests模块, 可以切换到python的安装目录找到 script文件夹 example: 进入cmd窗口切换到上面的目录直接运营下面两个命令中的一个 1. > Path\p ...