Description

Bob有一棵 \(n\) 个点的有根树,其中 \(1\) 号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。

定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。

Bob可能会进行这几种操作:

  • 1 x:把点 \(x\) 到根节点的路径上所有的点染上一种没有用过的新颜色。
  • 2 x y:求 \(x\) 到 \(y\) 的路径的权值。
  • 3 x:在以 \(x\) 为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行 \(m\) 次操作。

Input

第一行两个数 \(n,m\)。

接下来 \(n-1\) 行,每行两个数 \(a,b\),表示 \(a\) 与 \(b\) 之间有一条边。

接下来 \(m\) 行,表示操作,格式见题目描述。

Output

每当出现 \(2,3\) 操作,输出一行。

如果是 \(2\) 操作,输出一个数表示路径的权值。

如果是 \(3\) 操作,输出一个数表示权值的最大值。

Sample Input

5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5

Sample Output

3
4
2
2

HINT

共10个测试点

测试点1,\(1\leq n,m\leq1000\)。

测试点2、3,没有 \(2\) 操作。

测试点4、5,没有 \(3\) 操作。

测试点6,树的生成方式是,对于 \(i~(2\leq i \leq n)\),在 \(1\) 到 \(i-1\) 中随机选一个点作为 \(i\) 的父节点。

测试点7,\(1\leq n,m\leq 50000\)。

测试点8,\(1\leq n \leq 50000\)。

测试点9、10,无特殊限制。

对所有数据,\(1\leq n \leq 10^5\)。

Solution

LCT,实边连接的是相同的颜色,轻边连接不同的颜色,那么点 \(x\) 到根的路径的权值即为经过的轻边的数量 \(+1\),对于操作 \(1\),直接 \(access(x)\),实边 \((fa[x],x)\) 变为轻边时,将子树 \(x\) 的权值整体 \(+1\),轻边 \((fa[x],x)\) 变为实边时,将子树 \(x\) 的权值整体 \(-1\)。

注意我们需要将其子树更改的节点应为当前 \(splay\) 中深度最小的那个,而不是 \(splay\) 的根。

对于操作 \(2\),答案为 \(val[x]+val[y]-2\times val[lca(x,y)]+1\),其中 \(val[x]\) 为 \(x\) 到根的路径的权值,用线段树维护。

操作 \(3\) 就是在线段树上查询区间最大值。

注意LCT的 \(fa\) 和树链剖分的 \(fa\) 不要设成同一个数组。

Code

#include <cstdio>
#include <algorithm> const int N = 100001;
struct Edge { int v, nxt; } e[N << 1];
int n, m, cnt, head[N], siz[N], dfn[N], fa[N], ff[N], dep[N], top[N], val[N], tot, son[N], mx[N << 2], tag[N << 2], ch[N][2]; int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
void adde(int u, int v) {
e[++tot].nxt = head[u], head[u] = tot, e[tot].v = v;
}
void dfs1(int u, int f) {
siz[u] = 1, fa[u] = ff[u] = f, dep[u] = dep[f] + 1;
for (int i = head[u]; i; i = e[i].nxt) if (e[i].v != f) {
dfs1(e[i].v, u), siz[u] += siz[e[i].v];
if (siz[e[i].v] > siz[son[u]]) son[u] = e[i].v;
}
}
void dfs2(int u, int t) {
dfn[u] = ++cnt, val[dfn[u]] = dep[u], top[u] = t;
if (son[u]) dfs2(son[u], t);
for (int i = head[u]; i; i = e[i].nxt)
if (e[i].v != fa[u] && e[i].v != son[u]) dfs2(e[i].v, e[i].v);
}
int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] > dep[top[y]]) x = fa[top[x]];
else y = fa[top[y]];
}
return dep[x] < dep[y] ? x : y;
}
void pushdown(int cur) {
mx[cur << 1] += tag[cur], tag[cur << 1] += tag[cur];
mx[cur << 1 | 1] += tag[cur], tag[cur << 1 | 1] += tag[cur];
tag[cur] = 0;
}
void build(int cur, int l, int r) {
if (l == r) { mx[cur] = val[l]; return; }
int mid = (l + r) >> 1;
build(cur << 1, l, mid), build(cur << 1 | 1, mid + 1, r);
mx[cur] = std::max(mx[cur << 1], mx[cur << 1 | 1]);
}
void update(int cur, int l, int r, int L, int R, int x) {
if (L <= l && r <= R) { mx[cur] += x, tag[cur] += x; return; }
int mid = (l + r) >> 1;
if (tag[cur]) pushdown(cur);
if (L <= mid) update(cur << 1, l, mid, L, R, x);
if (mid < R) update(cur << 1 | 1, mid + 1, r, L, R, x);
mx[cur] = std::max(mx[cur << 1], mx[cur << 1 | 1]);
}
int query1(int cur, int l, int r, int p) {
if (l == r) return mx[cur];
int mid = (l + r) >> 1;
if (tag[cur]) pushdown(cur);
if (p <= mid) return query1(cur << 1, l, mid, p);
return query1(cur << 1 | 1, mid + 1, r, p);
}
int query2(int cur, int l, int r, int L, int R) {
if (L <= l && r <= R) return mx[cur];
int mid = (l + r) >> 1, res = 0;
if (tag[cur]) pushdown(cur);
if (L <= mid) res = query2(cur << 1, l, mid, L, R);
if (mid < R) res = std::max(res, query2(cur << 1 | 1, mid + 1, r, L, R));
return res;
}
int get(int x) {
return ch[ff[x]][1] == x;
}
bool isroot(int x) {
return ch[ff[x]][0] != x && ch[ff[x]][1] != x;
}
void rotate(int x) {
int y = ff[x], z = ff[y], k = get(x);
if (!isroot(y)) ch[z][ch[z][1] == y] = x;
ch[y][k] = ch[x][k ^ 1], ch[x][k ^ 1] = y;
ff[ch[y][k]] = y, ff[y] = x, ff[x] = z;
}
void splay(int x) {
for (int y; !isroot(x); rotate(x))
if (!isroot(y = ff[x])) rotate(get(x) ^ get(y) ? x : y);
}
int find(int x) {
while (ch[x][0]) x = ch[x][0];
return x;
}
void access(int x) {
for (int y = 0, z; x; y = x, x = ff[x]) {
splay(x), z = find(ch[x][1]);
if (z) update(1, 1, n, dfn[z], dfn[z] + siz[z] - 1, 1);
ch[x][1] = y, z = find(y);
if (z) update(1, 1, n, dfn[z], dfn[z] + siz[z] - 1, -1);
}
}
int main() {
n = read(), m = read();
for (int i = 1, u, v; i < n; ++i) u = read(), v = read(), adde(u, v), adde(v, u);
dfs1(1, 0), dfs2(1, 1), build(1, 1, n);
while (m--) {
int opt = read(), x = read(), y;
if (opt == 1) access(x);
else if (opt == 2) y = read(), printf("%d\n", query1(1, 1, n, dfn[x]) + query1(1, 1, n, dfn[y]) - (query1(1, 1, n, dfn[lca(x, y)]) << 1) + 1);
else printf("%d\n", query2(1, 1, n, dfn[x], dfn[x] + siz[x] - 1));
}
return 0;
}

[BZOJ 4817] [SDOI 2017] 树点涂色的更多相关文章

  1. [sdoi 2017]树点涂色

    传送门 Description Bob 有一棵\(n\)个点的有根树,其中\(1\)号点是根节点.Bob 在每个节点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是,这条路径上的点(包括起点 ...

  2. 【BZOJ4817】树点涂色(LCT,线段树,树链剖分)

    [BZOJ4817]树点涂色(LCT,线段树,树链剖分) 题面 BZOJ Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义 ...

  3. [BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 692  Solved: 408[Submit][Status ...

  4. [Bzoj4817] [Sdoi2017]树点涂色 (LCT神题)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 629  Solved: 371[Submit][Status ...

  5. [Sdoi2017]树点涂色 [lct 线段树]

    [Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...

  6. 「SDOI2017」树点涂色 解题报告

    「SDOI2017」树点涂色 我sb的不行了 其实一开始有一个类似动态dp的想法 每个点维护到lct树上到最浅点的颜色段数,然后维护一个\(mx_{0,1}\)也就是是否用虚儿子的最大颜色 用个set ...

  7. P3703 [SDOI2017]树点涂色

    P3703 [SDOI2017]树点涂色 链接 分析: 首先对于询问,感觉是线段树维护dfs序,每个点记录到根的颜色个数.第二问差分,第三问区间取max. 那么考虑修改,每次将一个点的颜色变成和父节点 ...

  8. 【LG3703】[SDOI2017]树点涂色

    [LG3703][SDOI2017]树点涂色 题面 洛谷 题解 更博辣,更博辣!!! 猪年的第一篇博客 一次只能染根到\(x\),且染的颜色未出现过 这句话是我们解题的关键. 设\(x\)到根的颜色数 ...

  9. 【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]

    树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1 ...

随机推荐

  1. H5移动端rem适配

    /** * 移动端自适应 */ <meta name="viewport" content="width=device-width,user-scalable=no ...

  2. spring注解的相关配置

    一.<context:annotation-config> 和 <context:component-scan> <context:annotation-config&g ...

  3. Dynamics 365设置错误通知首选项的方法

    本人微信公众号:微软动态CRM专家罗勇 ,回复288或者20181205可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 使用 ...

  4. iOS---------显示和隐藏状态栏的网络活动标志

    //在向服务端发送请求状态栏显示网络活动标志: [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; ...

  5. 如何为 .NET Core CLI 启用 TAB 自动补全功能

    如何为 .NET Core CLI 启用 TAB 自动补全功能 Intro 在 Linux 下经常可以发现有些目录/文件名,以及有些工具可以命令输入几个字母之后按 TAB 自动补全,最近发现其实 do ...

  6. 【原】Java学习笔记004 - 运算符

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 运算符:对常量 或 ...

  7. Linux学习历程——Centos 7 touch命令

    一.命令介绍 touch 命令用于创建空白文件,以及设置文件的时间. ----------------------------------------------------------------- ...

  8. Python爬虫之pyquery库的基本使用

    # 字符串初始化 html = ''' <div> <ul> <li class = "item-0">first item</li> ...

  9. LeetCode算法题-Binary Tree Tilt(Java实现)

    这是悦乐书的第263次更新,第276篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第130题(顺位题号是563).给定二叉树,返回整棵树的倾斜度.树节点的倾斜被定义为所有 ...

  10. 英语口语练习系列-C04-学校生活

    连接到英语口语系列总分类 连接到上一章常用问句 登鹳雀楼 登鹳雀楼 唐代:王之涣 白日依山尽,黄河入海流. 欲穷千里目,更上一层楼. He is a fraternity brother. 他是兄弟会 ...