暑假D1 T2 便(then) (带权并查集)

【题目描述】

给出一个R*C 的棋盘.共有R 行C 列,R*C 个格子.现要在每个格子都填一个
负整数.使得任意一个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.

【数据范围】

第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.

分析

对于一个2*2的格子,从上到下,从左到右依次记为a,b,c,d,可以得到a-c=b-d,即两行之间同列的数的差不变。

于是先按y排序,对于排序后相邻的同一列的两个点判断,如果还没有关系(体现在不在一个连通块),就把它

们连起来并记录值的关系;在一个连通块,就判断是否满足关系。

然而本题还有一个限制为非负整数。首先是输入时的判断。其次则是填写之后是否出现负数的判断。考虑对于每

一连通块的代表行找到它里面最小的数,然后再对于每个代表找连通块内与他相差最多的行,两个值加起来就是

那一行最小的数,如果这个数是负数就不行。

这道题的毒瘤数据也是醉了

#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;
int t,n,r,c;
int v[maxn],fa[maxn];//v:父节点-子节点 
int mi1[maxn],mi2[maxn];
struct point{
    int x,y,w;
}p[maxn];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x= f ? -x : x;
}

bool cmp(point a,point b){
    return a.y<b.y;
}

int find(int x){
    if(fa[x]==x) return x;
    int t=find(fa[x]);
    v[x]+=v[fa[x]];
    return fa[x]=t;
}

bool check(int x,int wx,int y,int wy){
    int dx=find(x),dy=find(y);
    if(dx==dy) return wx+v[x]==wy+v[y];
    fa[dx]=dy;
    v[dx]=wy+v[y]-wx-v[x];//w[dx]=wx+v[x],w[dy]=wy+v[y]
    return true;
}

bool solve(){
    bool opt=false;
    read(r);read(c);read(n);
    for(int i=1;i<=n;i++){
      read(p[i].x);read(p[i].y);read(p[i].w);
      opt|=p[i].w<0;
    }
    if(opt) return false;
    memset(mi1,0x3f,sizeof(mi1));
    memset(mi2,0x3f,sizeof(mi2));
    for(int i=1;i<=r;i++) fa[i]=i,v[i]=0;
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<n;i++)
     if(p[i].y==p[i+1].y&&!check(p[i].x,p[i].w,p[i+1].x,p[i+1].w))
      return false;
    for(int i=1;i<=n;i++){
        int dx=find(p[i].x);
        mi1[dx]=min(mi1[dx],p[i].w+v[p[i].x]);
    }
    for(int i=1;i<=r;i++){
        int dx=find(i);
        mi2[dx]=min(mi2[dx],-v[i]);//关系的有向性 
    }
    for(int i=1;i<=n;i++)
     if(fa[i]==i&&mi1[i]+mi2[i]<0) return false;
    return true;
}

int main(){
    freopen("then.in","r",stdin);
    freopen("then.out","w",stdout);
    read(t);
    while(t--)
      printf("%s\n", solve() ? "Yes" : "No");
    return 0;
}
View Code

 

posted @ 2019-07-10 16:31  _JSQ  阅读(182)  评论(0编辑  收藏  举报