Description

Input
第一行一个正整数 n.
接下来 n − 1 行每行两个个正整数 u, v, 表示有一条连接 u, v 的道路.
接下来 n − 1 行每行两个个正整数 u, v, 表示有一条连接 u, v 的道路.
Output
一行, 表示最少需要的信标数量.
Sample Input
4 1 2 1 3 1 4
Sample Output
2 样例 1 解释 在村庄 3, 4 放置信标, 四个村庄接收到的定位序列分别为 [1, 1], [2, 2], [0, 2], [2, 0]. 如果只在 3 放置, 则村庄 2, 4 接收到的定位序列都是 [2], 不满足要求. 注意定位序列是有序的.
Data Constraint
样例 2
见下发文件中的 ex_beacon2.in/out.
见下发文件中的 ex_beacon2.in/out.
Hint
对于前 20% 的数据, n ≤ 10;
对于前 45% 的数据, n ≤ 40, 树的形态随机;
对于前 70% 的数据, n ≤ 5000;
对于另 5% 的数据, 不存在一个村庄连接着 3 条或以上的道路;
对于 100% 的数据, 1 ≤ n ≤ 1000000, 1 ≤ u, v ≤ n, 保证数据合法.
对于前 45% 的数据, n ≤ 40, 树的形态随机;
对于前 70% 的数据, n ≤ 5000;
对于另 5% 的数据, 不存在一个村庄连接着 3 条或以上的道路;
对于 100% 的数据, 1 ≤ n ≤ 1000000, 1 ≤ u, v ≤ n, 保证数据合法.
分析
我们先特判链的情况答案为 1, 然后找到任意一个度数大于 2 的节点, 可以证 明这个点一定不需要放置信标.
于是以这个点作根 O(n) 的贪心即可.
证明如下:
深度相同的点对证明同上, 只考虑深度不同的点对. 如果它们在一颗子树中, 由于度数大于 2 所 以一定有另一颗子树的一个信标把他们区分开.
如果在不同的子树中, 有两种情况: 一个在没放信标的子树中, 一个在放了的子树中.
显然还存在另一个子树放了信标, 由于深度不 同他们会被这个信标区分开.
两个都在放了信标的子树中. 如果根的度数大于 3 则同上. 度数等于 3 时, 如果他们没有被区分开, 一定是他们先汇集到了一个节点上, 然后走到同一个信标上.
这个点一定是一条奇链的中点, 且不是根 (由于深度不同), 是在两个子树之一中唯一的. 那么他们走到另一个信标就一定有一个点走 了冤枉路, 既另一个信标可以区分出他们.


#include <iostream> #include <cstdio> using namespace std; const int N=1e6+10; struct Edge { int u,v,nx; }g[2*N]; int cnt,list[N]; int deg[N],a[N]; int n; void Add(int u,int v) { g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt; } void Dfs(int u,int fa) { int c=0; for (int i=list[u];i;i=g[i].nx) if (g[i].v!=fa) { Dfs(g[i].v,u); a[u]+=a[g[i].v]; if (!a[g[i].v]) c++; } a[u]+=c-(c!=0); } int main() { freopen("beacon.in","r",stdin); freopen("beacon.out","w",stdout); scanf("%d",&n); if (n==1) { printf("0"); return 0; } for (int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); Add(u,v);Add(v,u); deg[u]++;deg[v]++; } int root=0; for (int i=1;i<=n;i++) if (deg[i]>2) { root=i; break; } if (!root) { printf("1"); return 0; } Dfs(root,0); printf("%d",a[root]); }