[BZOJ 4817] [SDOI 2017] 树点涂色

时间:2022-08-30 22:57:30

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. &lbrack;sdoi 2017&rsqb;树点涂色

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

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

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

  3. &lbrack;BZOJ4817&rsqb;&lbrack;SDOI2017&rsqb;树点涂色&lpar;LCT&plus;DFS序线段树&rpar;

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

  4. &lbrack;Bzoj4817&rsqb; &lbrack;Sdoi2017&rsqb;树点涂色 (LCT神题)

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

  5. &lbrack;Sdoi2017&rsqb;树点涂色 &lbrack;lct 线段树&rsqb;

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

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

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

  7. P3703 &lbrack;SDOI2017&rsqb;树点涂色

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

  8. 【LG3703】&lbrack;SDOI2017&rsqb;树点涂色

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

  9. 【BZOJ4817】【SDOI2017】树点涂色 &lbrack;LCT&rsqb;&lbrack;线段树&rsqb;

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

随机推荐

  1. Ajax发送POST请求SpringMVC页面跳转失败

    问题描述:因为使用的是SpringMVC框架,所以想使用ModelAndView进行页面跳转.思路是发送POST请求,然后controller层中直接返回相应ModelAndView,但是这种方法不可 ...

  2. 安卓第十一天笔记-Intent与inter-filter配置

    安卓第十一天笔记-Intent与inter-filter配置 Intent与inter-filter配置 1.Intent对象简述 Android应用中有包含三种重要组件:Activity,Servi ...

  3. 解决oralce 11g dg搭建报错:ORA-16664、ORA-16714、ORA-16810问题--转

    下面不是小编错误报告只是转了网络一篇,同时也解决了我的问题所以复制过来给各位参考. 最近在弄11g的dg时,遇到如下问题,记录下.首先在主上查看报如下错误: DGMGRL> show confi ...

  4. suse linux环境变量设置

    以在suse上安装jdk1.5为例说明: 安装jdk1.5完毕后,就可以配置环境变量了. su  root XXXXXX // 键入管理员密码 对于suse来说,只需在/etc/profile 文件后 ...

  5. Twitter 工程师谈 JVM 调优

    一. 调优需要关注的几个方面 内存调优 CPU 使用调优 锁竞争调优 I/O 调优 二. Twitter 最大的敌人:延迟 导致延迟的几个原因? 最大影响因素是 GC 其他的有:锁和线程调度.I/O. ...

  6. 1、&Tab;Linux中的root用户切换&lpar;转载&rpar;

    su和su - 的区别 大部分Linux发行版的默认账户是普通用户,而更改系统文件或者执行某些命令,需要root身份才能进行,这就需要从当前用户切换到root用户,Linux中切换用户的命令是su或s ...

  7. C&plus;&plus;学习笔记:多态篇之虚析构函数

    动态多态中存在的问题:可能会产生内存泄漏! 以下通过一个例子向大家说明问什么会产生内存泄漏: class Shape//形状类 { public: Shape(); virtual double ca ...

  8. VPS、虚拟主机、云主机的区别

    引用知乎网友通俗的例子解释: 1. 小王是深圳的一拆迁户,有钱任性:自己租了一块地皮[带宽],盖了一栋10000平方的房子[服务器],计划租给别人赚钱.2. 但是10000平方的房子太大,能租起的人太 ...

  9. 牛客训练赛25-A-因数个数

    题目链接https://www.nowcoder.com/acm/contest/158/A 无语...这题很迷啊,原谅我的菜,刚开始想用预处理欧拉筛和前缀和,可是这题太血崩了,这样一样要遍历,1-e ...

  10. 【OData】Odata能做什么?

    在我看来OData就是一个实现Rest full的框架.你可以使用它对server的资源进行操作.那么它能做什么? 1. 获取资源 var context = new DefaultContainer ...