https://blog.csdn.net/QTY2001/article/details/77507716
dalao真是太强啦
我的乱搞做法有考虑不周的地方,虽然我没有想清楚为什么最后统计小于0的情况只用走一遍
所以我写的是走了两遍
但是其他的还是理解到了,十分巧妙的思路
和一般的带权并查集类似
上方修改find的时候pushdown
可能我写的比较丑所以要开longlong就很痛苦
问题 B: 便
时间限制: 2 Sec 内存限制: 512 MB
提交: 50 解决: 14
题目描述
给出一个R*C的棋盘.共有R行C列,R*C个格子.现要在每个格子都填一个非负整数.使得任意一个2*2的正方形区域都满足这样的性质:左上角的数字+右下角的数字=左下角的数字+右上角的数字.有些格子已经确定,你不能更改其中的数字.其他格子的数字由你决定.
不难验证每个2*2的区域都是符合要求的.
Orbitingflea想要知道一个可行的填充棋盘的方案.但是这个方案可能很大.所以你只需对给定的棋盘判定是否存在至少一种可行的填充棋盘的方案.
输入
第一行输入一个T,表示数据组数。接下来T组数据。
每组数据的第1行2个整数R,C表示棋盘的大小.
第2行1个整数n表示已经被填好数字的格子的数目.
接下来n行每行3个整数ri,ci,ai,表示第ri行ci列的格子被填上了数字ai.
输出
T行.第i行是第i组数据的答案.有合法方案时输出一行Yes,没有时输出一行No.
样例输入
6
2 2 3
1 1 0
1 2 10
2 1 20
2 3 5
1 1 0
1 2 10
1 3 20
2 1 30
2 3 40
2 2 3
1 1 20
1 2 10
2 1 0
3 3 4
1 1 0
1 3 10
3 1 10
3 3 20
2 2 4
1 1 0
1 2 10
2 1 30
2 2 20
1 1 1
1 1 -1
样例输出
Yes
No
No
Yes
No
No
提示
第1个测试点,R=1
第2,3个测试点,R*C<=12,如果有解,保证存在一个解使得所有数字大小不超过2
第4,5个测试点,R=2
第6,7个测试点,R=3
第8个测试点,1<=R,C<=20
第9个测试点,1<=R,C<=100
对于全部测试点,1<=T<=6,1<=R,C,n<=100000,1<=ri<=R,1<=ci<=C,同一个格子不会多次被填上数字.ai是整数且绝对值不超过10^9.
这道题翻译成中文竟然叫 便。。。。→_→
对于这个棋盘,要满足l1+r2==l2+r1,换言之l1-l2==r1-r2.从左向右以此类推。
因此,对于某一行,它与另一行对应列的差值相等。那么,这就和并查集很像了。可以通过已确定的点,实现行列间的的比较。而且差具有传递性,所以完全可以用带权并查集。
那么举个例子,行1和行2确定了差值,行2和行3确定了,就可以间接确定行1和行3的差,就把行3连成行1的儿子。
如果有合并,此处考虑两个问题。
1,如果当前两行已经有同一个父亲,那么如果他们的当前差不等于他们与父亲确定的差值的差,那么就可以返回无解了。
2,对于父亲的合并cha[fx]=cha[x,y]+cha[y]-cha[x];其实也挺好理解的。。
这个处理完后,那么就不会再存在互相矛盾的情况了,但还有一种情况,就是出现值小于零。那么如何判断呢?
对于所有确定值的点,那么它可以给父亲行提供一个值,(因为行与行之间的差确定了,那么确定的点值就可以给父亲行对应列一个确定的值),那么求出这个最小值。然后找出父亲与儿子最大的那个差。这样就找到了此联通块中的最小值了。判断是否小于零即可。
#include<bits/stdc++.h>
#define RG register
using namespace std;
struct node
{
int a,b,c;
node(int aa=0,int bb=0,int cc=0)
{
a=aa;b=bb;c=cc;
}
}point[100005];
int t,n,m,q,pd[100005],vis[100005],father1[100005],father2[100005];
long long size1[100005],size2[100005],mina1[100005],mina2[100005];
bool flag;
bool cmp1(const node &a,const node &b)
{
return a.a<b.a;
}
bool cmp2(const node &a,const node &b)
{
return a.b<b.b;
}
int find1(int x)
{
if(x==father1[x]) return x;
int now=find1(father1[x]);
size1[x]+=size1[father1[x]];
return father1[x]=now;
}
int find2(int x)
{
if(x==father2[x]) return x;
int now=find2(father2[x]);
size2[x]+=size2[father2[x]];
return father2[x]=now;
}
bool union1(int x,int y,int val)
{
int r1=find1(x),r2=find1(y);
if(r1!=r2)
{
father1[r1]=r2;
size1[r1]=size1[y]+val-size1[x];
}
else
{
if(size1[x]-size1[y]!=val) return false;
}
return true;
}
bool union2(int x,int y,int val)
{
int r1=find2(x),r2=find2(y);
if(r1!=r2)
{
father2[r1]=r2;
size2[r1]=size2[y]+val-size2[x];
}
else
{
if(size2[x]-size2[y]!=val) return false;
}
return true;
}
int main()
{
freopen("then.in","r",stdin);
freopen("then.out","w",stdout);
cin>>t;
while(t--)
{
flag=false;
scanf("%d%d",&n,&m);
scanf("%d",&q);
for(RG int i=1;i<=q;i++)
{
scanf("%d%d%d",&point[i].a,&point[i].b,&point[i].c);
}
for(int i=1;i<=n;i++)
father1[i]=i,size1[i]=0;
for(int i=1;i<=m;i++)
father2[i]=i,size2[i]=0;
for(RG int i=1;i<=q;i++)
{
if(point[i].c<0)
{
flag=true;
break;
}
}
sort(point+1,point+q+1,cmp1);
for(int i=2;i<=q;i++)
if(point[i].a==point[i-1].a)
{
if(!union2(point[i-1].b,point[i].b,point[i].c-point[i-1].c))
{
flag=true;
break;
}
}
sort(point+1,point+q+1,cmp2);
for(int i=2;i<=q;i++)
if(point[i].b==point[i-1].b)
{
if(!union1(point[i-1].a,point[i].a,point[i].c-point[i-1].c))
{
flag=true;
break;
}
}
memset(mina1,127,sizeof(mina1));
memset(mina2,127,sizeof(mina2));
for(int i=1;i<=q;i++)
{
int pos=find1(point[i].a);
mina1[pos]=min(mina1[pos],point[i].c+size1[point[i].a]);
}
for(int i=1;i<=n;i++)
{
int pos=find1(i);
mina2[pos]=min(mina2[pos],-size1[i]);
}
for(int i=1;i<=n;i++)
if(father1[i]==i&&mina1[i]+mina2[i]<0)
{
flag=true;
break;
}
memset(mina1,127,sizeof(mina1));
memset(mina2,127,sizeof(mina2));
for(int i=1;i<=q;i++)
{
int pos=find2(point[i].b);
mina1[pos]=min(mina1[pos],point[i].c+size2[point[i].b]);
}
for(int i=1;i<=m;i++)
{
int pos=find2(i);
mina2[pos]=min(mina2[pos],-size2[i]);
}
for(int i=1;i<=m;i++)
if(father2[i]==i&&mina1[i]+mina2[i]<0)
{
flag=true;
break;
}
if(flag)
{
printf("No\n");
}
else
{
printf("Yes\n");
}
}
return 0;
}