带权并查集——bzoj 4500 矩阵

Description

有一个n*m的矩阵,初始每个格子的权值都为0,可以对矩阵执行两种操作:
1.选择一行,该行每个格子的权值加1或减1。
2.选择一列,该列每个格子的权值加1或减1。
现在有K个限制,每个限制为一个三元组(x,y,c),代表格子(x,y)权值等于c。
问是否存在一个操作序列,使得操作完后的矩阵满足所有的限制。
如果存在输出"Yes",否则输出"No"。

Input
先输入一个T(T <= 5)代表输入有T组数据,每组数据格式为:
第一行三个整数n, m, k (1 <= n, m,k <= 1000)。接下来k行,每行三个整数x, y, c。

Output
对于每组数据,输出Yes或者No。

Sample Input
2
2 2 4
1 1 0
1 2 0
2 1 2
2 2 2
2 2 4
1 1 0
1 2 0
2 1 2
2 2 1

Sample Output
Yes
No

solution

据说跟bzoj1202差不多,思路一样令人自闭
对于矩阵里的一个点我们可以看做这个点对于的行(x)(a),列(y)(b),即可以写作(a-b=c)的形式,当然(b)可以是负值,(哇差分约束!)所以我们可以用带权并查集实现。对于每一个(a-b=c)的形式,我们可以将(y)(x)(c)的权值合并。即:

      if (fx!=fy) {
            f[fy]=fx;
            d[fy]=c+d[x]-d[y];
      }

(y)接在(x)的后面所以是(c+d[x]),但更新的时候一定要更新(fy)(y)(fy)的距离是(d[y])所以减去(d[y])(全世界就我不知道系列)

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1005;
int T,n,m,k,d[N<<1],f[N<<1];

inline int find (int x) {
	if (f[x]==x)	return x;
	int rt=find(f[x]);
	d[x]+=d[f[x]];
	return f[x]=rt;
}

int main () {
#ifndef ONLINE_JUDGE
	freopen("4500.in","r",stdin);
	freopen("4500.out","w",stdout);
#endif
	scanf("%d",&T);
	while (T--) {
		scanf("%d%d%d",&n,&m,&k);
		bool tag=0;
		for (int i=1;i<=n+m;i++)	f[i]=i,d[i]=0;
		for (int i=1,x,y,c;i<=k;i++) {
			scanf("%d%d%d",&x,&y,&c);
			y+=n;
			if (tag)	continue;
			int fx=find(x);
			int fy=find(y);
			if (fx!=fy) {
				f[fy]=fx;
				d[fy]=c+d[x]-d[y];
			} else if (d[y]-d[x]!=c) {
				tag=1;
			}
		}
		if (tag)	printf("No
");
		else	printf("Yes
");
	}
	return 0;
}

原文地址:https://www.cnblogs.com/FridayZ/p/13812769.html