Processing math: 20%

P6185 [NOI Online #1 提高组] 序列 题解

岸芷汀兰·2021-07-06 19:44·75 次阅读

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,也可以在任意两点一加一减。所以,如果环的点权之和为偶数,就可以完成目标;若第二种边形成了一个偶环,则可以看成是二分图。二分图的左部点的点权和和右部点的点权和之差是个定值,所以如果两部的点权和相等,就可以完成目标。(特别地,自环是奇环,所以自环并不影响正确性。)

三、代码:

Copy
#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; }

posted @   蓝田日暖玉生烟  阅读(75)  评论(0编辑  收藏  举报
编辑推荐:
· .NET 依赖注入中的 Captive Dependency
· .NET Core 对象分配(Alloc)底层原理浅谈
· 聊一聊 C#异步 任务延续的三种底层玩法
· 敏捷开发:如何高效开每日站会
· 为什么 .NET8线程池 容易引发线程饥饿
阅读排行:
· .NET 9.0 使用 Vulkan API 编写跨平台图形应用
· 终于决定:把自己家的能源管理系统开源了!
· [.NET] 使用客户端缓存提高API性能
· AsyncLocal的妙用
· .NetCore依赖注入(DI)之生命周期
历史上的今天:
2019-07-06 【转】洛谷 P3722 [AH2017/HNOI2017]影魔 题解
点击右上角即可分享
微信分享提示