分类
标签
2-SAT AC自动机 BFS CDQ dfs DP fail树 FFT FFT&NTT FWT hash KD-Tree KMP LCA SPFA STL Tarjan Treap Trie 主席树 乱搞 二分 二分图匹配 二分答案 二维SPFA 交互 位运算 其他 最小生成树 分块 区间DP 半平面交 博弈论 可持久化 可持久化Trie树 后缀数组 图库 平衡树 并查集 插头DP 数学 数论 无旋Treap 日记 暴力 权值树状数组 栈 树DP 树套树 树状数组 树贪心 概率DP 模拟 欧拉定理 点分治 状压DP 生成函数 矩阵乘 线性规划 线段树 组合 网络流 群论 莫比乌斯反演 计算几何 贪心 费用流 高斯消元
928 字
5 分钟
便
题目描述
给出一个R*C的棋盘.共有R行C列,R*C个格子.现要在每个格子都填一个非负整数.使得任意一个2*2的正方形区域都满足这样的性质:左上角的数字+右下角的数字=左下角的数字+右上角的数字.有些格子已经确定,你不能更改其中的数字.其他格子的数字由你决定. 这是一个符合要求的3*3的棋盘:
1 | 2 | 3 |
2 | 3 | 4 |
4 | 5 | 6 |
不难验证每个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
题解
每一列差分后得到的结果相同.每一行差分后的 结果也相同.于是我们对行列分别用带权并查集维护行之间,列之间的差分关系.如果差分关系出现矛盾(两行之间的差 值可以推导出两种可能)则无解.如果差分关系没有矛盾,则需要求出整个矩阵中能推导出的最小值判断是否小于0.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int fa1[1000005], fa2[1000005];
long long w1[1000005], w2[1000005];
long long Min1[1000005], Min2[1000005];
struct Point
{
int x, y, v;
} a[1000005];
bool cmpx(const Point &A, const Point &B)
{
return A.x < B.x;
}
bool cmpy(const Point &A, const Point &B)
{
return A.y < B.y;
}
int find1(int x)
{
if (x == fa1[x])
return x;
int rt = find1(fa1[x]);
w1[x] += w1[fa1[x]];
return fa1[x] = rt;
}
int find2(int x)
{
if (x == fa2[x])
return x;
int rt = find2(fa2[x]);
w2[x] += w2[fa2[x]];
return fa2[x] = rt;
}
bool link1(int a, int b, int w)
{
if (find1(a) != find1(b))
{
int fa = find1(a), fb = find1(b);
fa1[fa] = fa1[fb];
w1[fa] = w + w1[b] - w1[a];
return 1;
}
else
return w1[a] == w + w1[b];
}
bool link2(int a, int b, int w)
{
if (find2(a) != find2(b))
{
int fa = find2(a), fb = find2(b);
fa2[fa] = fa2[fb];
w2[fa] = w + w2[b] - w2[a];
return 1;
}
else
return w2[a] == w + w2[b];
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
bool flag = 1;
int R, C;
scanf("%d%d", &R, &C);
for (int i = 1; i <= R; i++)
{
fa1[i] = i;
w1[i] = 0;
}
for (int i = 1; i <= C; i++)
{
fa2[i] = i;
w2[i] = 0;
}
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].v);
for (int i = 1; i <= n; i++)
if (a[i].v < 0)
flag = 0;
sort(a + 1, a + n + 1, cmpx);
for (int i = 1; i < n; i++)
if (a[i].x == a[i + 1].x)
if (!link2(a[i].y, a[i + 1].y, a[i + 1].v - a[i].v))
flag = false;
sort(a + 1, a + n + 1, cmpy);
for (int i = 1; i < n; i++)
if (a[i].y == a[i + 1].y)
if (!link1(a[i].x, a[i + 1].x, a[i + 1].v - a[i].v))
flag = false;
memset(Min1, 0x3f, sizeof(Min1));
memset(Min2, 0x3f, sizeof(Min2));
for (int i = 1; i <= n; i++)
{
int rt = find1(a[i].x);
Min1[rt] = min(Min1[rt], a[i].v + w1[a[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 (fa1[i] == i && Min1[i] + Min2[i] < 0)
flag = 0;
}
if(flag)
puts("Yes");
else
puts("No");
}
}