题意
有一棵
n
n
n 个点的树,现在要在树上放
k
k
k 个信号站,每个点
x
x
x 可以用一个
k
k
k 维向量表示,向量第
i
i
i 维表示
x
x
x 与第
i
i
i 个信号站的距离。求最小的
k
k
k,使得每个点的向量两两不同。
n
≤
1
0
5
n\le 10^5
n≤105
分析
注意到每个点连接的连通块中,至多有 1 1 1 个不包含任何信号站。反过来,若满足上述条件,则所有点都能被区分。
容易得到一个 O ( n 2 ) O(n^2) O(n2) 的做法:枚举根,强制根一定要放信号站。然后递归进行贪心,若超过 1 1 1 棵子树不包含信号站,则在其中 s − 1 s-1 s−1 棵中分别放一个信号站即可,其中 s s s 表示不包含信号站的子树数量。
发现若选定一个度数至少为 3 3 3 的点为根,则每个点的父亲所在的连通块中必然会包含信号站。然后套用上面的做法即可。时间复杂度 O ( n ) O(n) O(n)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, ans;
bool f[N];
vector<int> e[N];
void dfs(int x, int fa)
{
int s = 0;
for (int to : e[x]) if (to != fa)
{
dfs(to, x);
s += !f[to];
f[x] |= f[to];
}
if (s > 1) f[x] = 1, ans += s - 1;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
int x, y; scanf("%d%d", &x, &y);
x++; y++;
e[x].push_back(y); e[y].push_back(x);
}
int rt = 0;
for (int i = 1; i <= n; i++) if (e[i].size() > 2) {rt = i; break;}
if (!rt) {printf("%d\n", 1); return 0;}
dfs(rt, 0);
printf("%d\n", ans);
return 0;
}