【Luogu P4092】[HEOI2016/TJOI2016]树
链接:
题目大意:
一颗树,除根节点外初始都是白点,根节点是黑点。
每次染黑一个结点或者询问一个结点的最近黑色祖先。
\(1\leq n\leq 10^5\)
正文:
可以反着做。就是每次染白一个点。那么设 \(lba_i\) 表示第 \(i\) 点的最近黑色祖先,那么每次染色的时候,就把 \(lba_i\) 作为 \(i\) 的父亲。然后就能并查集维护了。
代码:
int n, m;
int head[N], tot;
struct edge
{
int to, nxt;
}e[N << 1];
void add (int u, int v)
{
e[++tot] = (edge){v, head[u]}, head[u] = tot;
}
struct node
{
char opt;
int val, ans;
}a[N];
int cnt[N];
int f[N], lba[N];
void dfs (int u, int fa)
{
if (cnt[u]) lba[u] = u;
else lba[u] = fa;
f[u] = fa;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa) continue;
dfs(v, u);
}
}
int UFind(int x) {return lba[x] == x? x: lba[x] = UFind(lba[x]);}
int main()
{
scanf ("%d%d", &n, &m);
for (int i = 1; i < n; i++)
{
int x, y;
scanf ("%d%d", &x, &y);
add (x, y), add (y, x);
}
cnt[1] = 1;
for (int i = 1; i <= m; i++)
{
a[i].opt = getchar();
while (a[i].opt != 'C' && a[i].opt != 'Q') a[i].opt = getchar();
scanf ("%d", &a[i].val);
if (a[i].opt == 'C') cnt[a[i].val] ++;
}
dfs (1, -1);
f[1] = 1;
for (int i = m; i; --i)
{
if (a[i].opt == 'Q')
a[i].ans = UFind (a[i].val);
else
{
--cnt[a[i].val];
if (!cnt[a[i].val]) lba[a[i].val] = f[a[i].val];
}
}
for (int i = 1; i <= m; i++)
if (a[i].opt == 'Q')
printf ("%d\n", a[i].ans);
return 0;
}