P6185 [NOI Online #1 提高组] 序列 题解
一、题目:
二、思路:
这道题是一道蓝题,我都没想出来😥。我真的是菜到家了,今年的 NOI 不打铁都奇怪。
首先可以发现,操作 2 具有可传递性。若 (a,b) 可以有操作 2,(b,c) 也可以有操作 2,那么 (a,c) 也可以等价地相当于有操作 2。所以我们可以将操作 1 和 2 看做两种无向边,使用并查集将第二种边的连通块缩成一个点。缩完点之后的图就只剩了第一种边。(注意,缩完点后的图是有自环的。)
设缩完点后的点权 \sum\limits b_x-a_x。
又可以发现,若第一种边形成了一个奇环,那么就可以在任意两点同时 +1 或 -1,也可以在任意两点一加一减。所以,如果环的点权之和为偶数,就可以完成目标;若第二种边形成了一个偶环,则可以看成是二分图。二分图的左部点的点权和和右部点的点权和之差是个定值,所以如果两部的点权和相等,就可以完成目标。(特别地,自环是奇环,所以自环并不影响正确性。)
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int MAXN = 1e5 + 5;
int n, m, a[MAXN], b[MAXN], fa[MAXN], idx, c[MAXN];
long long sum[MAXN], sumc[3];
pair<int, int>optb[MAXN];
vector<int>linker[MAXN];
int find(int x) {
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
x = find(x); y = find(y);
if (x != y) fa[x] = y;
}
bool dfs(int x, int col) {
c[x] = col;
bool ok = true;
sumc[col] += sum[x];
for (auto &y : linker[x]) {
if (c[y] == col) ok = false;
if (!c[y] && !dfs(y, 3 - col)) ok = false;
}
return ok;
}
int main() {
int T = read();
while (T --) {
n = read(); m = read();
for (int i = 1; i <= n; ++ i) sum[i] = sumc[i] = c[i] = 0, linker[i].clear(), fa[i] = i;
idx = 0;
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int i = 1; i <= n; ++ i) b[i] = read();
for (int i = 1; i <= m; ++ i) {
int t = read(), x = read(), y = read();
if (t == 2) merge(x, y);
else optb[++ idx] = { x, y };
}
for (int x = 1; x <= n; ++ x) {
sum[find(x)] += b[x] - a[x];
}
for (int i = 1; i <= idx; ++ i) {
int x = optb[i].first, y = optb[i].second;
linker[find(x)].push_back(find(y));
linker[find(y)].push_back(find(x));
}
bool flag = true;
for (int x = 1; x <= n; ++ x) {
if (!c[x] && find(x) == x) {
sumc[1] = sumc[2] = 0;
bool ok = dfs(x, 1);
if (ok) {
if (sumc[1] != sumc[2]) { flag = false; break; }
}
else {
if ((sumc[1] + sumc[2]) & 1) { flag = false; break; }
}
}
}
if (flag) puts("YES");
else puts("NO");
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】博客园携手 AI 驱动开发工具商 Chat2DB 推出联合终身会员
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET 依赖注入中的 Captive Dependency
· .NET Core 对象分配(Alloc)底层原理浅谈
· 聊一聊 C#异步 任务延续的三种底层玩法
· 敏捷开发:如何高效开每日站会
· 为什么 .NET8线程池 容易引发线程饥饿
· .NET 9.0 使用 Vulkan API 编写跨平台图形应用
· 终于决定:把自己家的能源管理系统开源了!
· [.NET] 使用客户端缓存提高API性能
· AsyncLocal的妙用
· .NetCore依赖注入(DI)之生命周期
2019-07-06 【转】洛谷 P3722 [AH2017/HNOI2017]影魔 题解