【ybt金牌导航5-1-3】【luogu P2486】树上染色 / 染色

树上染色 / 染色

题目链接:ybt金牌导航5-1-3 / luogu P2486

题目大意

给你一个树,然后点有颜色,要你维护两个操作。
把一条路径上的点的颜色改成某个值,查询一条路径的颜色段数量。
颜色段是指最长的颜色相同的段。

思路

看到树上路径,自然想到树链剖分。

然后你发现你就需要线段树维护区间最左最右的颜色,这个很好维护。
然后你也可以维护出区间颜色段的。
然后记得树链剖分的时候你将链合并时它们也可能缝合,那你每次要把这条链两端颜色找出来,然后拿去比较。

然后大概就是这样。

代码

#include<cstdio> #include<algorithm> using namespace std; struct node { int to, nxt; }e[200001]; int n, m, a[100001], x, y, z; int le[100001], KK, lcol, rcol; int fa[100001], son[100001], sz[100001], deg[100001]; int pl[100001], dfn[100001], top[100001], tmp; char op; struct Tree { int lc, rc, num, lazy; }tr[400001]; 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 dfs1(int now, int father) {//树链剖分 fa[now] = father; deg[now] = deg[father] + 1; sz[now] = 1; int maxn = 0; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { dfs1(e[i].to, now); sz[now] += sz[e[i].to]; if (sz[e[i].to] > maxn) { maxn = sz[e[i].to]; son[now] = e[i].to; } } } void dfs2(int now, int father) { if (son[now]) { pl[son[now]] = ++tmp; top[son[now]] = top[now]; dfn[tmp] = son[now]; dfs2(son[now], now); } for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father && e[i].to != son[now]) { pl[e[i].to] = ++tmp; top[e[i].to] = e[i].to; dfn[tmp] = e[i].to; dfs2(e[i].to, now); } } //线段树操作 void up(int now) { tr[now].lc = tr[now << 1].lc; tr[now].rc = tr[now << 1 | 1].rc; tr[now].num = tr[now << 1].num + tr[now << 1 | 1].num; if (tr[now << 1].rc == tr[now << 1 | 1].lc)//接合的地方下相同颜色,两颜色段合并,所以个数少了一个 tr[now].num--;//下面的很多地方减一都是处理这个 } void down(int now, int l, int r) { if (tr[now].lazy != -1 && l != r) { tr[now << 1].num = tr[now << 1 | 1].num = 1; tr[now << 1].lc = tr[now << 1].rc = tr[now].lazy; tr[now << 1 | 1].lc = tr[now << 1 | 1].rc = tr[now].lazy; tr[now << 1].lazy = tr[now << 1 | 1].lazy = tr[now].lazy; tr[now].lazy = -1; } } void build(int now, int l, int r) { if (l == r) { tr[now] = (Tree){a[dfn[l]], a[dfn[l]], 1, -1}; return ; } tr[now].lazy = -1; int mid = (l + r) >> 1; build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r); up(now); } void change(int now, int l, int r, int L, int R, int num) { if (L <= l && r <= R) { tr[now].lc = tr[now].rc = num; tr[now].num = 1; tr[now].lazy = num; return ; } down(now, l, r); int mid = (l + r) >> 1; if (L <= mid) change(now << 1, l, mid, L, R, num); if (mid < R) change(now << 1 | 1, mid + 1, r, L, R, num); up(now); } int query(int now, int l, int r, int L, int R, int &l_col, int &r_col) { if (L <= l && r <= R) { if (l == L) l_col = tr[now].lc;//找到这段区间最左最右的颜色 if (r == R) r_col = tr[now].rc; return tr[now].num; } down(now, l, r); int mid = (l + r) >> 1, re = 0; if (L <= mid) re += query(now << 1, l, mid, L, R, l_col, r_col); if (mid < R) re += query(now << 1 | 1, mid + 1, r, L, R, l_col, r_col); if (L <= mid && mid < R) { if (tr[now << 1].rc == tr[now << 1 | 1].lc) re--;//相同颜色缝合 } return re; } void color(int x, int y, int z) { int X = top[x], Y = top[y]; while (X != Y) { if (deg[X] < deg[Y]) { swap(X, Y); swap(x, y); } change(1, 1, tmp, pl[X], pl[x], z); x = fa[X]; X = top[x]; } if (deg[x] > deg[y]) swap(x, y); change(1, 1, tmp, pl[x], pl[y], z); } int ask_num(int x, int y) { int X = top[x], Y = top[y], re = 0, tmp1, tmp2; lcol = rcol = -2; while (X != Y) { if (deg[X] < deg[Y]) { swap(X, Y); swap(x, y); swap(lcol, rcol); } tmp1 = tmp2 = -4; re += query(1, 1, tmp, pl[X], pl[x], tmp1, tmp2); if (tmp2 == lcol) re--;//跟你前面走的链缝合 lcol = tmp1; x = fa[X]; X = top[x]; } if (deg[x] > deg[y]) swap(x, y), swap(lcol, rcol); tmp1 = tmp2 = -4; re += query(1, 1, tmp, pl[x], pl[y], tmp1, tmp2); if (tmp1 == lcol) re--;//对接处有两个可以缝合的地方 if (tmp2 == rcol) re--; return re; } int main() { scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i < n; i++) { scanf("%d %d", &x, &y); add(x, y); } dfs1(1, 0); pl[1] = ++tmp; top[1] = 1; dfn[tmp] = 1; dfs2(1, 0); build(1, 1, n); while (m--) { op = getchar(); while (op != 'C' && op != 'Q') op = getchar(); if (op == 'C') { scanf("%d %d %d", &x, &y, &z); color(x, y, z); continue; } if (op == 'Q') { scanf("%d %d", &x, &y); printf("%d\n", ask_num(x, y)); continue; } } return 0; }

__EOF__

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