傳播QTYdalao的正解
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;
}