bzoj5100 [POI2018]Plan metra 构造+二分

Description


有一棵n个点的无根树,每条边有一个正整数权值,表示长度,定义两点距离为在树上的最短路径的长度。
已知2到n-1每个点在树上与1和n的距离,请根据这些信息还原出这棵树。

第一行包含一个正整数n(2<=n<=500000),表示点数。
第二行包含n-2个正整数d(1,2),d(1,3),…,d(1,n-1),分别表示每个点到1的距离。
第三行包含n-2个正整数d(n,2),d(n,3),…,d(n,n-1),分别表示每个点到n的距离。
输入数据保证1<=d<=1000000。

若无解,输出NIE。
否则第一行输出TAK,接下来n-1行每行三个正整数u,v,c(1<=u,v<=n,1<=c<=1000000)
表示存在一条长度为c的连接u和v两点的树边。
若有多组解,输出任意一组。

Solution


考虑1到n这条链,设它的长度为mn,显然mn=min⁡{d(1,i)+d(i,n)}mn=\min\left\{d(1,i)+d(i,n)\right\}mn=min{d(1,i)+d(i,n)}。证明的话可以考虑反证一下
那么把在这条链上的点抠出来,剩余的点我们依次判断。
对于点i,我们有f=d(1,i)+d(i,n)−mn2f=\frac{d(1,i)+d(i,n)-mn}{2}f=2d(1,i)+d(i,n)mn,其中f表示i到链的距离,这样我们也就知道了与i相连的点到1的距离,可以二分出这个点来

当然还存在一种情况就是1和n直接相连,那么剩余的点∣d(1,i)−d(i,n)∣|d(1,i)-d(i,n)|d(1,i)d(i,n)全部相等,其余点要么连向1要么连向n

无解的情况比较多,要细心。。

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fi first
#define se second

typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=500055;

struct pair {
	LL fi,se,id;
	bool operator <(const pair &b) const {
		return fi<b.fi;
	}
} p[N],v[N];

std:: vector <pair> ans;

bool vis[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(LL x,LL y,LL w) {
	ans.push_back((pair) {x,y,w});
}

int main(void) {
	int n=read(),c=0; LL mn=1LL<<30;
	if (n==2) return 0&puts("TAK\n1 2 1");
	rep(i,2,n-1) p[i].fi=read();
	rep(i,2,n-1) p[i].se=read();
	rep(i,2,n-1) p[i].id=i;
	LL lxf=abs(p[2].fi-p[2].se);
	if (lxf) {
		bool flag=true;
		rep(i,3,n-1) {
			if (abs(p[i].fi-p[i].se)!=lxf) {
				flag=false;
				break;
			}
		}
		if (flag) {
			puts("TAK");
			printf("%d %d %lld\n", 1,n,lxf);
			rep(i,2,n-1) {
				if (p[i].fi<p[i].se) printf("%d %d %lld\n", 1,i,p[i].fi);
				else printf("%d %d %lld\n", n,i,p[i].se);
			}
			return 0;
		}
	}
	rep(i,2,n-1) mn=std:: min(mn,p[i].fi+p[i].se);
	rep(i,2,n-1) if (p[i].fi+p[i].se==mn) {
		v[++c]=p[i];
		vis[i]=true;
	}
	v[++c]=(pair) {0,mn,1};
	v[++c]=(pair) {mn,0,n};
	std:: sort(v+1,v+c+1);
	rep(i,2,c) {
		if (v[i-1].fi==v[i].fi) return 0&puts("NIE");
		add_edge(v[i-1].id,v[i].id,v[i].fi-v[i-1].fi);
	}
	rep(i,2,n-1) if (!vis[i]) {
		LL tmp=p[i].fi+p[i].se-mn;
		if (tmp&1) return 0&puts("NIE");
		tmp=p[i].fi-tmp/2;
		pair wjp=(pair) {tmp,0,0};
		int it=std:: lower_bound(v+1,v+c+1,wjp)-v;
		if (it>c) return 0&puts("NIE");
		if (v[it].fi!=tmp) return 0&puts("NIE");
		add_edge(v[it].id,i,p[i].fi-tmp);
	}
	puts("TAK");
	for (int i=0;i<ans.size();++i) {
		printf("%lld %lld %lld\n", ans[i].fi,ans[i].se,ans[i].id);
	}
	return 0;
}
内容概要:本文系统梳理了2025年数学前沿领域的研究动态与发展趋势,涵盖代数几何、数论、微分几何、拓扑学、偏微分方程、数学物理等多个核心方向,并介绍了当前国际数学研究的三大主流趋势:代数几何与数论、分析与偏微分方程、几何拓扑与表示论。文中重点报道了青年数学家王虹成功证明三维挂谷猜想的重大突破,以及韦东奕在偏微分方程与几何分析方面的研究成果,展现了中国数学界的崛起态势。同时,文档还涉及数学基础研究、应用数学、数学教育、期刊评价体系及国际数学强国格局等内容,引用大量视频、文章和权威资源,呈现数学学科的全貌与发展前景。; 适合人群:具备一定数学基础的本科生、研究生及科研工作者,关注数学前沿发展的教师、科技爱好者以及从事人工智能、物理、工程等相关领域并需数学支撑的专业人士。; 使用场景及目标:①了解2025年数学领域的重要突破与研究热点,如挂谷猜想的证明、朗兰兹纲领、拓扑数据分析等;②把握数学各分支的前沿方向与交叉应用,服务于科研选题、学术规划或跨学科研究;③获取权威学习资源与经典文献推荐,辅助数学学习与教学实践。; 阅读建议:此文档为信息聚合型资料,建议结合所列视频、书籍和论文深入拓展学习,重点关注核心突破案例(如王虹、韦东奕)与主流研究方向的演进脉络,宜以批判性思维梳理知识体系,避免碎片化阅读。
### BZOJ1728 Two-Headed Cows (双头牛) 的解题思路 #### 题目概述 BZOJ1728 是一道经典的图论问题,题目描述了一群双头牛之间的关系网络。每只双头牛可以看作是一个节点,而它们的关系则构成了边。目标是从这些关系中找出满足特定条件的最大子集。 此问题的核心在于利用 **二分查找** 和 **染色法** 来验证是否存在符合条件的子图结构[^1]。 --- #### 解题核心概念 ##### 1. 图模型构建 该问题可以通过无向图建模,其中每个顶点代表一只双头牛,边表示两只双头牛之间存在某种关联。最终的目标是在这个图中找到最大的独立集合(Independent Set),即任意两个顶点都不相连的一组顶点[^2]。 ##### 2. 二分查找的应用 为了高效求解最大独立集大小 \( k \),采用二分策略来逐步逼近最优解。具体来说,在区间 [0, n] 中通过不断调整上下界寻找可能的最大值 \( k \)[^3]。 ##### 3. 染色法验证可行性 对于当前假设的最大独立集大小 \( mid \),尝试从原图中选取恰好 \( mid \) 个顶点构成候选集合,并检查其是否形成合法的独立集。这一过程通常借助 BFS 或 DFS 实现,同时配合颜色标记技术区分已访问状态以及检测冲突情况[^4]。 以下是基于 Python 的伪代码实现: ```python from collections import deque def bfs_coloring(graph, start_node): queue = deque() color_map = {} # 初始化起点的颜色为 0 color_map[start_node] = 0 queue.append(start_node) while queue: current = queue.popleft() for neighbor in graph[current]: if neighbor not in color_map: # 给邻居分配相反的颜色 color_map[neighbor] = 1 - color_map[current] queue.append(neighbor) elif color_map[neighbor] == color_map[current]: return False # 如果发现相邻节点有相同颜色,则无法完成有效染色 return True def is_possible_to_select_k(graph, nodes_count, target_size): from itertools import combinations all_nodes = list(range(nodes_count)) possible_combinations = combinations(all_nodes, target_size) for subset in possible_combinations: subgraph = {node: [] for node in subset} valid_subset = True for u in subset: for v in graph[u]: if v in subset and v != u: subgraph[u].append(v) # 对子图进行染色测试 colors_used = set() coloring_success = True for node in subset: if node not in colors_used: success = bfs_coloring(subgraph, node) if not success: coloring_success = False break if coloring_success: return True # 找到一个有效的组合即可返回成功标志 return False def binary_search_max_independent_set(graph, total_nodes): low, high = 0, total_nodes best_result = 0 while low <= high: mid = (low + high) // 2 if is_possible_to_select_k(graph, total_nodes, mid): best_result = mid low = mid + 1 else: high = mid - 1 return best_result ``` --- #### 复杂度分析 上述算法的时间复杂度主要取决于以下几个方面: - 枚举所有可能的子集规模:\( O(\binom{n}{k}) \), 其中 \( k \) 表示当前试探的独立集大小。 - 子图构造与染色检验操作:每次调用 `bfs_coloring` 函数需遍历整个子图,最坏情况下时间开销接近线性级别 \( O(k^2) \). 综合来看整体效率较高但仍有优化空间[^5]. --- #### 总结 通过对 BZOJ1728 进行深入剖析可知,合理运用二分加染色的方法能够显著提升解决问题的能力。这种方法不仅适用于本题场景下寻找最大独立集的任务需求,同时也可推广至其他相似类型的 NP 完全难题处理之中[^6]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值