4551: [Tjoi2016&Heoi2016]树
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 748 Solved: 394
[Submit][Status][Discuss]
Description
在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下
两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个
结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
先)你能帮帮他吗?
Input
输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v
有一条有向边接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询
问操作对于每次询问操作,1 ≤ N, Q ≤ 100000。
Output
输出一个正整数,表示结果
Sample Input
5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3
Sample Output
1
2
2
1
2
2
1
HINT
新加数据9组(By HFLSyzx ),未重测--2016.8.2
Source
分析
先离线读入所有的操作,统计每个点的标记次数,用并查集构建每个点向上最近的标记点。倒序处理操作,如果是查询,直接输出并查集的答案即可;否则该点标记次数减1,如果刚好变成0,就把并查集连向其父亲结点。
代码
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int N = 200005; 6 7 int n, m; 8 9 int tot; 10 int hd[N]; 11 int nt[N]; 12 int to[N]; 13 14 int ope[N]; 15 int num[N]; 16 int cnt[N]; 17 int ans[N]; 18 19 char oper[5]; 20 21 int fa[N], f[N]; 22 23 int find(int u) 24 { 25 return fa[u] == u ? u : fa[u] = find(fa[u]); 26 } 27 28 void prework(int u, int fr) 29 { 30 f[u] = fr; 31 32 if (cnt[u]) 33 fa[u] = u; 34 else 35 fa[u] = find(fr); 36 37 for (int i = hd[u]; ~i; i = nt[i]) 38 if (to[i] != fr)prework(to[i], u); 39 } 40 41 signed main(void) 42 { 43 scanf("%d%d", &n, &m); 44 45 memset(hd, -1, sizeof(hd)); 46 47 for (int i = 1; i < n; ++i) 48 { 49 int x, y; scanf("%d%d", &x, &y); 50 51 nt[tot] = hd[x]; to[tot] = y; hd[x] = tot++; 52 nt[tot] = hd[y]; to[tot] = x; hd[y] = tot++; 53 } 54 55 ++cnt[1]; 56 57 for (int i = 1; i <= m; ++i) 58 { 59 scanf("%s%d", oper, num + i); 60 61 if (oper[0] == 'C') 62 ope[i] = 1, ++cnt[num[i]]; 63 } 64 65 prework(1, 1); 66 67 for (int i = m; i >= 1; --i) 68 { 69 if (ope[i]) 70 { 71 --cnt[num[i]]; 72 if (!cnt[num[i]]) 73 fa[num[i]] = find(f[num[i]]); 74 } 75 else 76 ans[i] = find(num[i]); 77 } 78 79 for (int i = 1; i <= m; ++i) 80 if (!ope[i])printf("%d ", ans[i]); 81 }
@Author: YouSiki