1、题意:
给定一颗有根树(根为1),有以下
两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个
结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
先)你能帮帮他吗?
2、分析:本来想直接上dfs序+线段树的,后来一看= =|||,woc..这个离线就是并查集啊,= =|||,离线,倒着做,然后遇到标记删没了的时候就和父亲合并
#include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; #define M 200010 inline int read(){ char ch = getchar(); int x = 0, f = 1; while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while('0' <= ch && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } struct Edge{ int u, v, next; } G[M]; int head[M], tot; int fa[M], father[M]; int mark[M], res[M]; struct query{ int op, num; } a[M]; inline void add(int u, int v){ G[++ tot] = (Edge){u, v, head[u]}; head[u] = tot; } inline int find(int x){ return fa[x] == x ? x : fa[x] = find(fa[x]); } inline void dfs(int x){ if(mark[x]) fa[x] = x; else fa[x] = find(father[x]); for(int i = head[x]; i != -1; i = G[i].next) if(G[i].v != father[x]){ father[G[i].v] = x; dfs(G[i].v); } } int main(){ int n = read(), Q = read(); memset(head, -1, sizeof(head)); for(int i = 1; i < n; i ++){ int u = read(), v = read(); add(u, v); add(v, u); } char ch[5]; mark[1] = 1; for(int i = 1; i <= Q; i ++){ scanf("%s", ch); if(ch[0] == 'C'){ a[i].op = 1; a[i].num = read(); mark[a[i].num] ++; } else a[i].op = 2, a[i].num = read(); } dfs(1); for(int i = Q; i >= 1; i --){ if(a[i].op == 1){ mark[a[i].num] --; if(!mark[a[i].num]) fa[a[i].num] = find(father[a[i].num]); } else{ res[i] = find(a[i].num); } } for(int i = 1; i <= Q; i ++){ if(a[i].op == 2) printf("%d\n", res[i]); } return 0; }