[Kattis Boxes] 倍增法LCA / DFS序
题目链接:【Virtual Judge】 【Kattis Boxes】
题目描述:
There are N boxes, indexed by a number from 1 to N. Each box may (or not may not) be put into other boxes. These boxes together form a tree structure (or a forest structure, to be precise).
You have to answer a series of queries of the following form: given a list of indices of the boxes, find the total number of boxes that the list of boxes actually contain.
Consider, for example, the following five boxes.
- If the query is the list “1”, then the correct answer is “5”, because box 1 contains all boxes.
- If the query is the list “4 5”, then the correct answer is “2”, for boxes 4 and 5 contain themselves and nothing else.
- If the query is the list “3 4”, then the correct answer is “2”.
- If the query is the list “2 3 4”, then the correct answer is “4”,
since box 2 also contains box 5. - If the query is the list “2”, then the correct answer is “3”,
because box 2 contains itself and two other boxes.
Input
The first line contains the integer N(1≤N≤200000,1≤N≤200000) , the number of boxes.
The second line contains NNintegers. The iith integer is either the index of the box which contains the i th box, or zero if the iith box is not contained in any other box.
The third line contains an integer
Output
For each query, output a line which contains an integer representing the total number of boxes.
中文题意:
解题思路:由于箱子与箱子之间是一对多的关系,那么这
N
个箱子可以看成一个森林(即多棵树)。然后有两种方法:
- 比较简单的做法是先求出DFS序(时间戳),按DFS序编号,然后就能把子树对应到区间,那么,最后会得到
k 个区间,求区间并。- 另外一种比较zuo的做法是LCA搞。首先求出所有的树上节点对应的子树的大小
sz
,节点深度
dep
,以及LCA的初始化
fa 。然后按照节点深度对 M 个节点按照节点排序。然后,对于M 个点,LCA去重,依次加上节点为根节点的子树的大小即可。
参考代码1:
/** * LCA倍增法 */ #include <bits/stdc++.h> using namespace std; const int MX = 200000 + 5; const int MM = 20 + 5; struct Edge { int v, next; } edge[MX]; int head[MX], tot; int N, Q, M, buf[MM], IDU[MX]; int ans; struct TNode { int sz, dep, root; int fa[MM]; } d[MX]; void init() { tot = 0; memset(head, -1, sizeof(head)); memset(IDU, 0, sizeof(IDU)); } void add_edge(int u, int v) { edge[tot] = Edge{v, head[u]}; head[u] = tot ++; } void dfs(int u, int k, int& rt) { int v; d[u].sz = 1; d[u].dep = k; d[u].root = rt; for(int i = head[u]; ~i; i = edge[i].next) { v = edge[i].v; d[v].fa[0] = u; for(int j = 1; j < MM; j++) d[v].fa[j] = d[d[v].fa[j - 1]].fa[j - 1]; dfs(v, k + 1, rt); d[u].sz += d[v].sz; } } bool cmp(const int& a, const int& b) { return d[a].dep < d[b].dep; } int LCA(int u, int v) { int i, j; if(d[u].dep < d[v].dep) swap(u, v); for(i = 0; (1 << i) <= d[u].dep; i ++); i--; for(j = i; j >= 0; j --) { if(d[u].dep - (1 << j) >= d[v].dep) u = d[u].fa[j]; } if(u == v) return u; for(j = i; j >= 0; j --) { if(d[u].fa[j] != d[v].fa[j] && d[u].fa[j] != u) { u = d[u].fa[j], v = d[v].fa[j]; } } return d[u].fa[0]; } bool chosen(const int& u, const int& v) { if(d[u].root != d[v].root) return false; int lca = LCA(u, v); if(u == lca || v == lca) return true; return false; } int main() { int u, v; while(~scanf("%d", &N)) { init(); for(v = 1; v <= N; v ++) { scanf("%d", &u); if(u == 0) continue; add_edge(u, v); IDU[v] ++; } for(u = 1; u <= N; u ++) { if(IDU[u]) continue; for(int i = 0; i < MM; i++) d[u].fa[i] = u; dfs(u, 1, u); } scanf("%d", &Q); while(Q --) { scanf("%d", &M); for(int i = 0; i < M; i++) { scanf("%d", &buf[i]); } sort(buf, buf + M, cmp); ans = 0; for(int i = 0; i < M; i++) { u = buf[i]; bool ok = true; for(int j = 0; j < i; j++) { v = buf[j]; if(chosen(u, v)) { ok = false; break; } } if(ok) ans += d[u].sz; } printf("%d\n", ans); } } return 0; }
参考代码2:
/** * DFS序 */ #include <bits/stdc++.h> using namespace std; const int MX = 200000 + 5; const int MM = 20 + 5; int N, Q, M; struct Edge { int v, next; } edge[MX]; int head[MX], tot; int dfn[MX], id, R[MX], L[MX], ans; bool flag[MX]; void init() { tot = 0; id = 0; memset(head, -1, sizeof(head)); memset(flag, false, sizeof(flag)); } void add_edge(int u, int v) { edge[tot] = Edge{v, head[u]}; head[u] = tot ++; } void dfs(int u) { int v; L[u] = dfn[u] = ++ id; /// [L, R] for(int i = head[u]; ~i; i = edge[i].next) { v = edge[i].v; dfs(v); } R[u] = id; } struct INode { int s, t; int get() { return t - s + 1; } bool operator < (const INode& e) const { if(s == e.s) return t < e.t; return s < e.s; } } I[MM]; int calc(int M) { int ret = 0, cur = 0; sort(I, I + M); for(int i = 0; i < M; i++) { if(cur >= I[i].t) continue; if(cur < I[i].s) cur = I[i].t, ret += I[i].get(); else if(cur < I[i].t) cur = I[i].t, ret += I[i].t - cur; } return ret; } int main() { int u, v; while(~scanf("%d", &N)) { init(); for(v = 1; v <= N; v++) { scanf("%d", &u); if(u == 0) continue; add_edge(u, v); flag[v] = true; } for(u = 1; u <= N; u++) { if(flag[u]) continue; dfs(u); } scanf("%d", &Q); while(Q --) { scanf("%d", &M); ans = 0; for(int i = 0; i < M; i++) { scanf("%d", &u); I[i] = INode{L[u], R[u]}; } ans = calc(M); printf("%d\n", ans); } } return 0; }
- 另外一种比较zuo的做法是LCA搞。首先求出所有的树上节点对应的子树的大小
sz
,节点深度
dep
,以及LCA的初始化