PROBLEM
建立最少的信标,使得任意两个点到至少一个信标的距离不同
SOLUTION
当n>1时,信标的个数>0,于是我们可以枚举其中一个信标的位置,将这个树的根就定为这个点,那么点就分层互不影响。
考虑当我们在根上放信标后,我们在一个点上放信标,对于它的子树还是依照原先的分层,没有影响,对于它到根的路径,可以分成到它的距离不同的若干层,就相当于将它到根节点的路径割开,那么最终我们就要使得同一层(根据深度)之间没有连边,那么就可以满足条件。
很容易想到一个性质,我们将信标放到非叶子节点,将其到根的路径割开,必然不如在它的子树中割开,割开的路径多,于是我们可以谈心地想到,放信标的节点在叶子上。
于是我们对于每条链的贡献是1,一个节点的贡献为儿子贡献之和,如果有一条链将其最后断开,那么当我们将其他儿子处理完后这条链就不用再计算了,所以判断一下是否有链,如果有的话贡献-1。
最后我们再枚举根节点,就可以做到N^2
还有一种想法:每个点若有c个儿子,那么它的子树中放的信标的个数至少c-1,因为如果有两个儿子下没有信标,那么到这个点再到另一个子树的信标的距离应该是一样的。
如果我们不枚举根节点怎么做呢?
有结论:一个度数大于2的节点一定可以不放信标。
假设我们把它当做根,那么根至少有c-1=2个儿子有信标。
证明其的可行性。
对于同深度的点,用上述方法可以解决,对于不同深度的点(u,v):
1.u与v在根节点的同一子树内,由于深度不同可以上到根中的另一个子树的信标
2.u与v在不同子树内,如果两个子树都有信标就必然可以区分,如果有一个没有那么也可以保证另一个一定有,向那个信标走即可区分了。
于是我们可以证这个节点为根一定可以满足要求。再考虑我们的贪心策略,即可贪心出正确答案,做到O(N)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 1000050
#define maxm 2000050
using namespace std;
int n,i,j,x,y,du[maxn],cnt[maxn],rt;
int em,e[maxm],nx[maxm],ls[maxn],f[maxn];
void insert(int x,int y){
em++; e[em]=y;nx[em]=ls[x];ls[x]=em;
em++; e[em]=x;nx[em]=ls[y];ls[y]=em;
}
void dg(int x,int p){
if (du[x]==1&&x!=p) cnt[x]=1; else cnt[x]=0;
f[x]=0; int tot=0;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
dg(e[i],x);
cnt[x]+=cnt[e[i]];
if (cnt[e[i]]==1) tot++;
f[x]+=f[e[i]];
}
f[x]+=max(0,tot-1);
}
int main(){
freopen("beacon.in","r",stdin);
freopen("beacon.out","w",stdout);
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d%d",&x,&y);
insert(x,y);
du[x]++,du[y]++;
}
for(i=1;i<=n;i++) if (du[i]>=3) {rt=i;break;}
if (!rt){
if (n==1) printf("0"); else printf("1");
return 0;
}
dg(rt,rt);
printf("%d",f[rt]);
}