树点涂色
luogu 3703
题目大意
给出一棵树,每个节点的初始颜色不同,做若干操作:
1.在一个点到根节点路径上染上一种新的颜色
2.查询一条路径上有多少种不同的颜色
3.查询一个点x,使该点到根节点路径的不同颜色种数最多
输入样例
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
输出样例
3
4
2
2
数据范围
1⩽n,m⩽1051\leqslant n,m\leqslant 10^51⩽n,m⩽105
解题思路
对于该图,可以建立LCT,虚边连接的是不同颜色的点,实边连接的是相同颜色的点,然后对此进行操作
操作1和access操作相似
对于修改一个点x的颜色
对于修改链中的点,本来x贡献为1,修改后为0,所以修改链深度大于y的p-1
而对于x原来所在链上的点,本来x贡献为0,修改后为1,所以该链上深度大于y的p+1
操作2相当于x点到lca的不同颜色种数加上y点到lca的不同颜色种数
就是px+py−2×plca+1p_x+p_y-2\times p_{lca}+1px+py−2×plca+1
操作3可以按dfs序建立线段树,这样就可以查询子树内的最小值了
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 100010
using namespace std;
int n, m, x, y, z, w, tot, s[N<<2], fa[N], bg[N], ed[N], dep[N], lazy[N<<2], head[N], son[N][2], faa[N][20];
struct rec
{int to, next;
}a[N<<1];
void add(int x, int y)
{a[++tot].to = y;a[tot].next = head[x];head[x] = tot;return;
}
void downtate(int x)//线段树
{if (!lazy[x]) return;s[x * 2] += lazy[x], lazy[x * 2] += lazy[x];s[x * 2 + 1] += lazy[x], lazy[x * 2 + 1] += lazy[x];lazy[x] = 0;return;
}
void up(int x)
{s[x] = max(s[x * 2], s[x * 2 + 1]);return;
}
void change(int x, int L, int R, int l, int r, int y)
{if (L == l && R == r){s[x] += y;lazy[x] += y;return;}downtate(x);int mid = (L + R) >> 1;if (r <= mid) change(x * 2, L, mid, l, r, y);else if (l > mid) change(x * 2 + 1, mid + 1, R, l, r, y);else change(x * 2, L, mid, l, mid, y), change(x * 2 + 1, mid + 1, R, mid + 1, r, y);up(x);return;
}
int ask(int x, int L, int R, int l, int r)
{if (L == l && R == r) return s[x];int mid = (L + R) >> 1;downtate(x);if (r <= mid) return ask(x * 2, L, mid, l, r);else if (l > mid) return ask(x * 2 + 1, mid + 1, R, l, r);else return max(ask(x * 2, L, mid, l, mid), ask(x * 2 + 1, mid + 1, R, mid + 1, r));
}
bool NR(int x)//LCT
{return son[fa[x]][0] == x || son[fa[x]][1] == x;
}
bool IRS(int x)
{return son[fa[x]][1] == x;
}
void rotate(int x)
{int y = fa[x], z = fa[y], k = IRS(x), g = son[x][!k];if (NR(y)) son[z][IRS(y)] = x;if (g) fa[g] = y;son[x][!k] = y;son[y][k] = g;fa[x] = z;fa[y] = x;return;
}
void Splay(int x)
{while(NR(x)){if (NR(fa[x])) rotate(IRS(x) == IRS(fa[x]) ? fa[x] : x);rotate(x);}return;
}
int find_root(int x)
{while(son[x][0]) x = son[x][0];return x;
}
void access(int x)
{for (int y = 0; x; x = fa[y = x]){Splay(x);int z = son[x][1];if (z) z = find_root(z), change(1, 1, n, bg[z], ed[z], 1);//对原链上的点贡献+1if (y) z = find_root(y), change(1, 1, n, bg[z], ed[z], -1);//对修改的脸上的点贡献-1son[x][1] = y;}return;
}
void dfs(int x)//求dfs序
{bg[x] = ++w;dep[x] = dep[fa[x]] + 1;change(1, 1, n, bg[x], bg[x], dep[x]);for (int i = 1; i <= 16; ++i)faa[x][i] = faa[faa[x][i - 1]][i - 1];for (int i = head[x]; i; i = a[i].next)if (!bg[a[i].to]){faa[a[i].to][0] = fa[a[i].to] = x;dfs(a[i].to);}ed[x] = w;
}
int lca(int x, int y)
{if (dep[x] < dep[y]) swap(x, y);for (int i = 16; i >= 0; --i)if (dep[faa[x][i]] >= dep[y]) x = faa[x][i];for (int i = 16; i >= 0; --i)if (faa[x][i] != faa[y][i]) x = faa[x][i], y = faa[y][i];return x == y ? x : faa[x][0];
}
int main()
{scanf("%d%d", &n, &m);for (int i = 1; i < n; ++i){scanf("%d%d", &x, &y);add(x, y);add(y, x);}dfs(1);while(m--){scanf("%d", &x);if (x == 1){scanf("%d", &x);access(x);}else if (x == 2){scanf("%d%d", &x, &y);z = lca(x, y);x = ask(1, 1, n, bg[x], bg[x]);y = ask(1, 1, n, bg[y], bg[y]);z = ask(1, 1, n, bg[z], bg[z]);printf("%d\n", x + y - z * 2 + 1);}else{scanf("%d", &x);printf("%d\n", ask(1, 1, n, bg[x], ed[x]));}}return 0;
}