【luogu P3703】树点涂色

树点涂色

题目链接:luogu P3703

题目大意

给你一个树,每个点有颜色,初始互不相同,根节点是 1,要你维护几个操作。
给一个从根出发的路径上点染上一个新的颜色。
询问一个路径中的点有多少种颜色。
找在一个子树中找一个点使得它到根节点的路径上的点的颜色种数最多。

思路

首先我们看到染色一定是从根节点出发的一条路径。

那我们会联想到 LCT 的 access 操作。
那我们可以想到将 LCT 的虚边看做是连着两个颜色不同的边,实边是连着颜色相同的。

那接着要怎么求权值呢?
我们考虑先看看第三问怎么搞,那看到子树最大,我们考虑用一个线段树,把原本的树按 dfs 序排。
然后我们维护从起点到某个点要经过的颜色数,然后放在线段树上就是区间内点这个值的最小值。
这样我们会发现一个子树会在连续的 dfs 序上,就可以直接线段树查询了。

那接着求两个点之间,那容易想到直接单点查询它们两个的值和它们的 LCA 的值,然后 fx+fy2×fLCA+1

但是接着是容易想到染色操作就不是单纯的普通 access 操作了。
那你想想你一染色会发生什么。
你会不停地找一段又一段连续的颜色,然后打通它们,把不同颜色的边变成虚的。
那变成虚的那部分就是要颜色数加一,那你变成实的就是要颜色数减一,线段树搞搞就好。

然后就可以了。

代码

#include<cstdio> #include<algorithm> using namespace std; struct node { int to, nxt; }e[200001]; int n, m, l[100001], r[100001], le[100001], KK; int tmpp, op, x, y, fa[100001]; int v[400001], lz[400001], fath[100001][21]; int deg[100001], st[100001], ed[100001]; void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; e[++KK] = (node){x, le[y]}; le[y] = KK; } //线段树部分 void up_(int now) { v[now] = max(v[now << 1], v[now << 1 | 1]); } void down_(int now) { if (!lz[now]) return ; v[now << 1] += lz[now]; v[now << 1 | 1] += lz[now]; lz[now << 1] += lz[now]; lz[now << 1 | 1] += lz[now]; lz[now] = 0; } void add(int now, int l, int r, int L, int R, int val) { if (L <= l && r <= R) { v[now] += val; lz[now] += val; return ; } down_(now); int mid = (l + r) >> 1; if (L <= mid) add(now << 1, l, mid, L, R, val); if (mid < R) add(now << 1 | 1, mid + 1, r, L, R, val); up_(now); } int query(int now, int l, int r, int L, int R) { if (L <= l && r <= R) return v[now]; down_(now); int mid = (l + r) >> 1, re = 0; if (L <= mid) re = query(now << 1, l, mid, L, R); if (mid < R) re = max(re, query(now << 1 | 1, mid + 1, r, L, R)); return re; } //LCT 部分 bool nrt(int now) { return l[fa[now]] == now || r[fa[now]] == now; } bool ls(int now) { return l[fa[now]] == now; } void rotate(int x) { int y = fa[x]; int z = fa[y]; int b = (ls(x) ? r[x] : l[x]); if (z && nrt(y)) (ls(y) ? l[z] : r[z]) = x; if (ls(x)) r[x] = y, l[y] = b; else l[x] = y, r[y] = b; fa[x] = z; fa[y] = x; if (b) fa[b] = y; } void Splay(int x) { while (nrt(x)) { if (nrt(fa[x])) { if (ls(x) == ls(fa[x])) rotate(fa[x]); else rotate(x); } rotate(x); } } int get_root(int x) { while (l[x]) x = l[x]; return x; } void access(int x) { int lst = 0; for (; x; x = fa[x]) { Splay(x); int tmp = r[x]; if (tmp) {//把不是在链那边的别的分支都加一个颜色 tmp = get_root(tmp); add(1, 1, n, st[tmp], ed[tmp], 1); } if (lst) {//把自己的链都减(每次找到的是同一个颜色的,所以减一就行) tmp = get_root(lst); add(1, 1, n, st[tmp], ed[tmp], -1); } r[x] = lst; lst = x; } } int LCA(int x, int y) { if (deg[x] < deg[y]) swap(x, y); for (int i = 20; i >= 0; i--) if (deg[fath[x][i]] >= deg[y]) x = fath[x][i]; if (x == y) return x; for (int i = 20; i >= 0; i--) if (fath[x][i] != fath[y][i]) { x = fath[x][i]; y = fath[y][i]; } return fath[x][0]; } void dfs(int now, int father) {//初始的 dfs fath[now][0] = father; fa[now] = father; st[now] = ++tmpp; deg[now] = deg[father] + 1; add(1, 1, n, st[now], st[now], deg[now]); for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) dfs(e[i].to, now); ed[now] = tmpp; } int main() { scanf("%d %d", &n, &m); for (int i = 1; i < n; i++) { scanf("%d %d", &x, &y); add(x, y); } dfs(1, 0); for (int i = 1; i <= 20; i++) for (int j = 1; j <= n; j++) fath[j][i] = fath[fath[j][i - 1]][i - 1]; while (m--) { scanf("%d", &op); if (op == 1) { scanf("%d", &x); access(x); continue; } if (op == 2) { scanf("%d %d", &x, &y); int lca = LCA(x, y); printf("%d\n", query(1, 1, n, st[x], st[x]) + query(1, 1, n, st[y], st[y]) - 2 * query(1, 1, n, st[lca], st[lca]) + 1); continue; } if (op == 3) { scanf("%d", &x); printf("%d\n", query(1, 1, n, st[x], ed[x])); continue; } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P3703.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(68)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示