Lowest Common Ancestor
在 IOICamp2017 解過的題目,雖然名稱叫做「最低共同祖先」,但是並不真的要求出 LCA (?)
在一棵樹上有 n 個點 ,對每個點 ,找出有幾組有序點對的 LCA 是 。
第一行一個正整數 ,代表測資筆數。每筆測資兩行, 第一行整數 ,第二行 個整數 ,代表 的老爸是 。
輸出 個以空白分隔的整數 ,代表有 組有序點對的 LCA 是 。
對於樹上一組點對的 LCA ,可以想成這一組點對要走訪到對方,必定會經過 LCA , 所以要求出以某節點當作 LCA 的點對數,只要先求出它底下所有子樹大小就可以進一步求出。
現在要求一個點作為 LCA 的組合數,
有幾種情況需要考慮:
注意:是「有序」點對。
我的程式碼裡面有稍微變化一下算式,不過意義是一樣的。 也就是
其中, 代表編號 的節點要算的點對數量, 代表編號 的節點的子樹大小(包含自己), 代表編號 節點的孩子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long int UT;
const int MAXN=100010;
UT sz[MAXN];
vector<int> g[MAXN];
UT dfs(int u) {
UT size=1;//self
for(int i=0; i<g[u].size(); ++i) {
size += dfs(g[u][i]);
}
return (sz[u]=size);
}
int main(void) {
int T, n;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i=2; i<=n; ++i) {
int v;
scanf("%d",&v);
g[v].push_back(i);
}
dfs(1);
//for(int i=1; i<=n; ++i) printf("?? %lld\n", sz[i]);
for(int i=1; i<=n; ++i) {
if(i!=1) putchar(' ');
UT X = sz[i]-1, sum;
sum = X*2+1;
for(int j=0; j<g[i].size(); ++j) {
sum +=( (X-sz[g[i][j]])*sz[g[i][j]] );
}
printf("%lld",sum);
}
puts("");
for(int i=0; i<=n; ++i) g[i].clear();
}
return 0;
}
Written on February 12th, 2017 by Kuang-Yu Jeng後記:當初為什麼要這麼麻煩,題目都給 parent 了我還在建圖…