[DFS][贪心]JZOJ 5944 信标
分析
我们先特判链的情况答案为 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]); }
在日渐沉没的世界里,我发现了你。