JZOJ5944.【NOIP2018模拟11.01】信标

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
#include
#include
#include
#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=3) {rt=i;break;}
	if (!rt){
		if (n==1) printf("0"); else printf("1");
		return 0;
	}
	dg(rt,rt);
	printf("%d",f[rt]);
}

你可能感兴趣的:(题解,图论,贪心,结论题,树)