并查集的再次进阶,从实际情况中分离出模型

今天我们再来谈一谈并查集的深度运用:
从实际问题中分离出并查集的模型
这是我们机房模拟赛的DST2:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
乍一看我们对这道题一脸懵逼,不过不要慌,先来分析一下这道题的要求:
来看一看一个2x2的棋盘:
a b
c d

  • 1个棋盘如果合法,那么就满足c+b=a+d移项就会发现a-b=c-d
    仔细一想不就是每一列之间的差都为定值吗?
    同列也可以得到每一行之间的差也相等。
  • “对于任意一列,两行之间的差相等”是一个很重要的性质.这告诉我们:每一列差分后得到的结果相同.每一行差分后的结果也相同.
    然而到这里就很明了了。
  • 于是我们对行列分别用带权并查集维护行之间,列之间的差分关系.如果差分关系出现矛盾(两行之间的差值可以推导出两种可能)则无解.
  • 但是题目中还有一个限制,表中填的数必须是非负整数。所以如果查分关系没有错误的话,还需要求出整个表中的最小值是否小于0.
  • 所以,代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define PN "then"
#define GG 	{puts("No");continue;}
const int maxn=100005;
typedef long long ll;
int ufs1[maxn];ll w1[maxn];
int ufs2[maxn];ll w2[maxn];
struct node{
	int x,y,val;
	void read(){
		scanf("%d%d%d",&x,&y,&val);
	}
}P[maxn];
bool cmpx(const node &A,const node &B){
	return A.x<B.x;
}
bool cmpy(const node &A,const node &B){
	return A.y<B.y;
}
int find1(int x){
	if(x==ufs1[x])return x;
	int rt=find1(ufs1[x]);
	w1[x]+=w1[ufs1[x]];
	return ufs1[x]=rt;
}
int find2(int x){
	if(x==ufs2[x])return x;
	int rt=find2(ufs2[x]);
	w2[x]+=w2[ufs2[x]];
	return ufs2[x]=rt;
}
bool link1(int a,int b,ll w){
	if(find1(a)!=find1(b)){
		int ra=find1(a),rb=find1(b);
		ufs1[ra]=ufs1[rb];
		w1[ra]=w+w1[b]-w1[a];
		return true;
	}else{
		return w1[a]==w+w1[b];
	}
}
bool link2(int a,int b,ll w){
	if(find2(a)!=find2(b)){
		int ra=find2(a),rb=find2(b);
		ufs2[ra]=ufs2[rb];
		w2[ra]=w+w2[b]-w2[a];
		return true;
	}else{
		return w2[a]==w+w2[b];
	}
}

ll Min1[maxn],Min2[maxn];
int main(){
	freopen(PN".in","r",stdin);
	freopen(PN".out","w",stdout);
	int tests;scanf("%d",&tests);	
	while(tests--){
		bool flag=true;
		int R,C;scanf("%d%d",&R,&C);
		for(int i=1;i<=R;++i){
			ufs1[i]=i;w1[i]=0;
		}
		for(int i=1;i<=C;++i){
			ufs2[i]=i;w2[i]=0;
		}
		int n;scanf("%d",&n);
		for(int i=1;i<=n;++i)P[i].read();
		for(int i=1;i<=n;++i)if(P[i].val<0)flag=false;
		sort(P+1,P+n+1,cmpx);
		for(int i=1;i<n;++i)
			if(P[i].x==P[i+1].x)
				if(!link2(P[i].y,P[i+1].y,P[i+1].val-P[i].val))flag=false;
		sort(P+1,P+n+1,cmpy);
		for(int i=1;i<n;++i)
			if(P[i].y==P[i+1].y)
				if(!link1(P[i].x,P[i+1].x,P[i+1].val-P[i].val))flag=false;

		memset(Min1,0x3f,sizeof(Min1));
		memset(Min2,0x3f,sizeof(Min2));
		for(int i=1;i<=n;++i){
			int rt=find1(P[i].x);
			Min1[rt]=min(Min1[rt],P[i].val+w1[P[i].x]);
		}
		for(int i=1;i<=R;++i){
			int rt=find1(i);
			Min2[rt]=min(Min2[rt],-w1[i]);
		}
		for(int i=1;i<=R;++i)
			if(ufs1[i]==i&&Min1[i]+Min2[i]<0)
		flag=false‘ 
		printf("%s\n",flag?"Yes":"No");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值