マス目と整数 题解
前言
题意简述
给你一个
题目分析
第一步肯定是找性质,我们从
对其推广,对于
我们发现,还可以进一步拓展结论:由于
我们想到做前缀和后,变为差分约束搞,但是显然不太对。我们想到可以使用带权并查集来维护,这种套路在带权并查集是常见的,不做赘述。可以对于每一列,依次把相邻的两个有值的位置所在的行进行合并(这里相邻指的是中间没有其他有值的位置)。
除了合并的时候无解,我们注意到题目中还有非负整数的限制。我们对于每一列的每一个有值的位置,可以通过并查集中的边,推断出和它处在同一个联通块内,所有行对应位置的值,我们仅需要保证这些能被唯一确定的点值均非负即可,剩下的位置是不确定的。那么把每个连通块路径压缩成一个菊花后,我们只需要维护并查集联通块内,到父亲边权的最值即可,这个可以预处理。具体判断逻辑见代码。
于是本题做完了,时间复杂度带一个并查集的小常数。如果使用搜索可以避免,但是会进入复杂度陷阱,拥有一个大常数。可以对于每一列新建一个虚拟点,这样可以减少码量和常数,建议在理解基础算法后再尝试理解,两种代码都给出,供读者学习。
代码
普通实现
#include <cstdio> #include <iostream> #include <vector> #include <algorithm> using namespace std; #define BAD "No" #define OK "Yes" const int N = 1e5 + 10; using lint = long long; int n, m, q; vector<pair<int, int>> vec[N]; int fa[N]; lint d[N], mi[N]; int get(int x) { if (x == fa[x]) return x; int f = get(fa[x]); d[x] += d[fa[x]]; return fa[x] = f; } inline void merge(int x, int y, lint v) { int a = get(x), b = get(y); if (a == b) return d[x] - d[y] != v && (puts(BAD), exit(0), 0), void(); d[a] = d[y] - d[x] + v, fa[a] = b; } signed main() { #ifndef XuYueming freopen("zibi.in", "r", stdin); freopen("zibi.out", "w", stdout); #endif scanf("%d%d%d", &n, &m, &q); for (int x, y, v; q--; ) { scanf("%d%d%d", &x, &y, &v); vec[y].emplace_back(x, v); } for (int i = 1; i <= n; ++i) fa[i] = i, mi[i] = 0x3f3f3f3f3f3f3f3f; for (int i = 1; i <= m; ++i) { sort(vec[i].begin(), vec[i].end()); for (int lst = 0, lv = 0; auto [x, v] : vec[i]) { if (lst) merge(x, lst, v - lv); lst = x, lv = v; } } for (int i = 1; i <= n; ++i) get(i), mi[fa[i]] = min(mi[fa[i]], d[i]); for (int i = 1; i <= m; ++i) { for (auto [x, v] : vec[i]) { if (-d[x] + v + mi[fa[x]] < 0) return puts(BAD), 0; } } puts(OK); return 0; }
虚拟点实现
#include <cstdio> #include <iostream> using namespace std; #define BAD "No" #define OK "Yes" const int N = 1e5 + 10; using lint = long long; int n, m, q; int fa[N << 1]; lint d[N << 1], mi[N << 1]; int get(int x) { if (x == fa[x]) return x; int f = get(fa[x]); d[x] += d[fa[x]]; return fa[x] = f; } inline void merge(int x, int y, lint v) { int a = get(x), b = get(y); if (a > b) swap(a, b), swap(x, y), v = -v; if (a == b) return d[x] - d[y] != v && (puts(BAD), exit(0), 0), void(); d[a] = d[y] - d[x] + v, fa[a] = b; } signed main() { #ifndef XuYueming freopen("zibi.in", "r", stdin); freopen("zibi.out", "w", stdout); #endif scanf("%d%d%d", &n, &m, &q); for (int i = 1; i <= n + m; ++i) fa[i] = i, mi[i] = 0x3f3f3f3f3f3f3f3f; for (int x, y, v; q--; ) { scanf("%d%d%d", &x, &y, &v); merge(y + n, x, -v); } for (int i = 1; i <= n + m; ++i) get(i); for (int i = 1; i <= n; ++i) mi[fa[i]] = min(mi[fa[i]], d[i]); for (int i = 1; i <= m; ++i) if (-d[i + n] + mi[fa[i + n]] < 0) return puts(BAD), 0; puts(OK); return 0; }
卡常后最优解代码
#include <cstdio> #include <cstdlib> using namespace std; const int MAX = 1 << 26; char buf[MAX], *inp = buf; template <typename T> inline void read(T &x) { x = 0; char ch = *inp++; for (; ch < 48; ch = *inp++); for (; ch >= 48; ch = *inp++) x = (x << 3) + (x << 1) + (ch ^ 48); } inline void swap(int &a, int &b) { a ^= b ^= a ^= b; } #define BAD "No" #define OK "Yes" const int N = 1e5 + 10; using lint = long long; int n, m, q; int fa[N << 1]; lint d[N << 1], mi[N << 1]; int get(int x) { return x == fa[x] ? x : (get(fa[x]), d[x] += d[fa[x]], fa[x] = fa[fa[x]]); } inline void merge(int x, int y, lint v) { int a = get(x), b = get(y); if (a > b) swap(a, b), swap(x, y), v = -v; if (a == b) return d[x] - d[y] != v && (puts(BAD), exit(0), 0), void(); d[a] = d[y] - d[x] + v, fa[a] = b; } signed main() { fread(buf, 1, MAX, stdin), read(n), read(m), read(q); for (int i = 1; i <= n + m; ++i) fa[i] = i, mi[i] = 0x3f3f3f3f3f3f3f3f; for (int x, y, v; q--; ) read(x), read(y), read(v), merge(y + n, x, -v); for (int i = 1; i <= n + m; ++i) get(i); for (int i = 1; i <= n; ++i) mi[fa[i]] > d[i] && (mi[fa[i]] = d[i]); for (int i = 1; i <= m; ++i) if (-d[i + n] + mi[fa[i + n]] < 0) return puts(BAD), 0; puts(OK); return 0; }
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18548183。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】