JZOJ 5944

题目大意

给你一棵树,可以每个点都安放定位器,
要求你放尽量少的定位器
使得任意两点(u, v)都至少有一个点 x ,使得 dst[x, u] != dst[x, v]


 简单的手玩后发现,一棵以 x 为根的子树,x 有 k 个儿子

那么在 x 的子树中 至少要保证有 k - 1 个儿子的子树中有定位器

否则一定会冲突的,由于他们到 lca 距离是一定的,
那么 lca 外的子树中同深度的点就无法区分了

发现有的树会出锅,是一个定位器的祖先两端的点出问题了,
他们到定位器距离是一样的,深度是不同的,那就先强制往根放一个

这样深度不同的点就区分开了

这样 n^2 的做有 70pts

我也只会 70pts

考虑什么做法能降复杂度,并不会

要么推出来下面的性质要么找规律

可以做到 O(n) ,只要找到一个度数大于 2 的点为根,根不放,进行上边的贪心即可

考虑证明它

 

假设现在一棵根度数为奇数的子树中,有两点无法区分

根据深度分类讨论一下

先考虑两点深度相同的时候
由于任意一点 k 个儿子中 k - 1 个的子树都放有定位器,所以是不存在这种情况的

接下来是深度不同的情况,还要分三种情况考虑

一种是两个点在同一棵子树中
显然还至少会有一个定位器在别的子树中,将他们区分开来

一种是一个点所在的子树中没有定位器而另一个有
同上,至少会有一个定位器在别的子树中将他们区分开来

另一种是两个点所在的子树中都有定位器
如果根的度数 > 3,同上,至少会有一个定位器在别的子树中将他们区分开来
如果度数 = 3,那他们肯定是走到了 奇链中点 处,而且这个点是唯一的
所以一定还有另一个定位器,且它到两点的距离不同

所以找一个度数大于 2 的点为根 O(n) 贪心的做就好了

找不到就是链, puts(“1”);


 代码:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
using namespace std;

const int MAX_N = 1000005;

struct EDGE{
	int nxt, to;
	explicit EDGE(int NXT = 0, int TO = 0) {nxt = NXT; to = TO;}
}edge[MAX_N << 1];
int n, tot_edge, ans, bgn;
int head[MAX_N], deg[MAX_N];

inline int rd() {
	register int x = 0, c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) {
		x = x * 10 + (c ^ 48);
		c = getchar();
	}
	return x;
}
inline void add(int x, int y) {
	edge[++tot_edge] = EDGE(head[x], y);
	head[x] = tot_edge;
	++deg[x];
	edge[++tot_edge] = EDGE(head[y], x);
	head[y] = tot_edge;
	++deg[y];
	return;
}
bool dfs(int x, int frm) {
	int tot = 0;
	for (int i = head[x]; i; i = edge[i].nxt) if (edge[i].to != frm) {
		int y = edge[i].to;
		tot += dfs(y, x);
	}
	if (frm == 0) {
		if (tot < deg[x] - 1) {
			ans += (deg[x] - 1 - tot);
			tot += (deg[x] - 1 - tot);
		}
	} else if (tot < deg[x] - 2) {
		ans += (deg[x] - 2 - tot);
		tot += (deg[x] - 2 - tot);
	}
	return (tot != 0);
}

int main() {
	freopen("beacon.in", "r", stdin);
	freopen("beacon.out", "w", stdout);
	n = rd();
	register int xx, yy;
	for (int i = 1; i < n; ++i) {
		xx = rd(); yy = rd();
		add(xx, yy);
	}
	if (n == 1) {
		puts("0");
		return 0;
	}
	for (int i = 1; i <= n; ++i) {
		if (deg[i] > 2) {
			bgn = i;
			break;
		}
	}
	if (!bgn) {
		puts("1");
		return 0;
	}
	dfs(bgn, 0);
	printf("%d\n", ans);
	return 0;
}

 

posted @ 2018-11-02 19:39  EvalonXing  阅读(202)  评论(0编辑  收藏  举报