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;
}

 

转载于:https://www.cnblogs.com/xcysblog/p/9894075.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值