带权并查集——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\n");
else printf("Yes\n");
}
return 0;
}