Codeforces 1850H:The Third Letter 带权并查集

发布时间 2023-08-04 17:13:49作者: Trilliverse

1850H.The Third Letter


Description:

  • \(n\) 个人,\(m\) 个条件,每次给出两个人 \(a_i\)\(b_i\) 一维的位置关系,以距离 \(d_i\) 表示,其正负表示方位的前后,判断这 \(m\) 个条件是否矛盾。(不同的人可以在横轴上位于同一点)

Analysis:

  • 将所有的横轴位置抽象为一个点,用带权并查集维护点与点之间的链接关系。
  • 对于一般的并查集来说,可以维护集合内的联通关系,进而求解不同集合之间的问题(如计数等)或判断图的联通性。带权并查集主打一个边权的维护(即额外的信息)。
  • 路径压缩\(DSU\) 的重要一环,相当于建立起每个子节点和根节点的直接关系,这便优化了查找效率,无须从某一节点持续向上递归(一旦树退化成了一条链,岂不是寄了!!)
int find(int x) {
	if(x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
	// return fa[x] = find(fa[x]) 
}
  • 那带权并查集呢?注意,我们要维护的不是题中所给的 \(d\),而是要动态维护起每个点到其父节点(在路径压缩下也就是根节点)的 \(w[i]\),这里就产生了对根节点有影响的两个过程:
  1. \(find\)\(x\))过程:因路径压缩,节点 \(x\) 原来的 \(fa[x]\) 更新为根节点,那么 \(w[x]\) 记录的子节点到父节点的距离 也要相应更新为到根节点(也就是新的父节点)
  2. 合并过程:因集合合并,两个集合各自的根节点变为一个大集合的一个根节点,自然而然,其中一个集合要进行权值的更新。

image
image


Solution:

int fa[maxn];
ll w[maxn];
 
int find(int x) {
	if(x != fa[x]) {
		// 记录原来的父节点,从而记录原来的父节点 t 到根节点的距离w[t]
		int t = fa[x];
		fa[x] = find(fa[x]);
		w[x] += w[t]; // 路径压缩的权值更新
	}
	return fa[x];
}
 
void solve() {
	int n,m; cin >> n >> m;
	// 初始化
	for(int i=1;i<=n;i++) {
		fa[i] = i; // 我是我的父亲
		w[i] = 0;  // 到父节点(目前是自己)的距离
	}
	bool flag = true; // 标记目前是否已经出现矛盾
	while(m--) {
		int u,v; ll d;
		cin >> u >> v >> d;
		if(!flag) continue;
		
		int fu = find(u);
		int fv = find(v);
		if(fu != fv) {
			fa[fu] = fv;
			w[fu] = d + w[v] - w[u]; //集合合并的权值更新
		}
		else { // fu == fv 说明二者距离可求,与所给的d比较即可
			if(w[u] - w[v] != d) flag = false;
		}
	}
	if(flag) cout << "YES" << endl;
	else cout << "NO" << endl;
}