【构造】0602 Koxia and Game

news/2025/2/11 4:37:55/

子问题1

题意:

给定三个长度为 n n n 的数组 a , b , c a,b,c a,b,c 1 ≤ a i , b i , c i ≤ n 1\leq a_i,b_i,c_i\leq n 1ai,bi,cin。两个人 A l i c e Alice Alice B o b Bob Bob 分别拿数 n n n 次,第 i i i 次拿数时,两人从 { a i , b i , c i } \{a_i,b_i,c_i\} {ai,bi,ci} 三个数中选择两个,每次拿数 A l i c e Alice Alice 先手选择一个,拿完后 B o b Bob Bob 从剩下的两个数中选择一个。问是否有 A l i c e Alice Alice 的一种拿数选择,使得拿完后 B o b Bob Bob 的数恰好是一个 1 − n 1-n 1n 的排列。

数据范围: 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105

题解:

首先,在每个位置 A l i c e Alice Alice 拿完数后,至少有一个位置使得 B o b Bob Bob 有两种选择(即剩下的两个数不相同),这样, B o b Bob Bob 必然就可以有一种选择使得自己拿到的 n n n 个数构不成排列。

证明:假设存在一种选择方式使得 B o b Bob Bob 拿到的 n n n 个数为一个排列。因为 B o b Bob Bob 至少存在一个位置两个数不同,故找到这个位置,将数字换成另一个,此时 B o b Bob Bob 选择的 n n n 个数就构不成排列了。

因此, A l i c e Alice Alice 拿完数后,每个位置剩下的数都得是两个相同的数 x i x_i xi,且 x i x_i xi 各不相同,则 B o b Bob Bob 选择的 n n n 个数就是一个排列了,否则 B o b Bob Bob 选择的 n n n 个数至少有一种选择使得其不为排列。

代码:

#include <bits/stdc++.h>
using namespace std;void solve() {int n;cin >> n;vector<int> a(n), b(n), c(n);for (int i = 0; i < n; ++i) cin >> a[i], a[i] -= 1;for (int i = 0; i < n; ++i) cin >> b[i], b[i] -= 1;for (int i = 0; i < n; ++i) cin >> c[i], c[i] -= 1;vector<int> cnt(n);bool ok = true;for (int i = 0; i < n; ++i) {vector<int> vec = {a[i], b[i], c[i]};sort(vec.begin(), vec.end());if (vec[1] != vec[0] && vec[1] != vec[2]) {ok = false;break;} else {cnt[vec[1]] += 1;}}if (ok) {for (int i = 0; i < n; ++i) {if (cnt[i] == 0) {ok = false;break;}}}if (ok) cout << "YES\n";else cout << "NO\n";
}int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int T = 1;cin >> T;while (T--) solve();return 0;
}

子问题2

题意:

给定两个长度为 n n n 的数组 a , b a,b a,b 1 ≤ a i , b i ≤ n 1\leq a_i,b_i\leq n 1ai,bin。需要构造一个长度为 n n n 的数组 c c c 1 ≤ c i ≤ n 1\leq c_i\leq n 1cin。两个人 A l i c e Alice Alice B o b Bob Bob 分别拿数 n n n 次,第 i i i 次拿数时,两人从 { a i , b i , c i } \{a_i,b_i,c_i\} {ai,bi,ci} 三个数中选择两个,每次拿数 A l i c e Alice Alice 先手选择一个,拿完后 B o b Bob Bob 从剩下的两个数中选择一个。问是否有 A l i c e Alice Alice 的一种拿数选择,使得拿完后 B o b Bob Bob 的数恰好是一个 1 − n 1-n 1n 的排列。

数据范围: 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105

题解:
相较于子问题1,子问题2需要构造一个 c c c,满足 B o b Bob Bob 拿的数恰好是一个 1 − n 1-n 1n 的排列。

可以知道的是,如果 a i = b i a_i=b_i ai=bi,则 c i c_i ci 为什么数都无所谓了。
如果存在 a i = b i = a j = b j , i ≠ j a_i=b_i=a_j=b_j,i\neq j ai=bi=aj=bj,i=j,则无论怎么构造 c c c B o b Bob Bob 都有至少一种选择方式使得拿到的数构不成一个排列。

故我们现在面对的局面是,有 k k k a i = b i a_i=b_i ai=bi,且这 k k k a i a_i ai 各不相同,其中 0 ≤ k ≤ n 0\leq k\leq n 0kn

对于剩下的 n − k n-k nk 个位置,其需要填充剩下的数,这里剩下的数是指除了 k k k a i a_i ai 以外其他的数。

故我们需要思考的是如何在 n − k n-k nk 个位置填充剩下的 n − k n-k nk 个数,使得我们选择的一定是一个排列?

将问题转换为图论,对于 a i , b i a_i,b_i ai,bi,考虑在 a i a_i ai b i b_i bi 之间连一条边。要确定这条边的方向,如果是从 a i a_i ai 指向 b i b_i bi ,表示选择 b i b_i bi,否则表示选择 a i a_i ai,我们需要对每条边都选择一个方向,使得每个点的入度都恰好为 1 1 1,对于 a i = b i a_i=b_i ai=bi 的情况也可以一并操作。

由于一共 n n n 个点, n n n 条边,可能构成 m m m 个连通分量。对于每个连通分量,其中的点数和边数必须相同,否则就没办法使得每个点的入度都为 1 1 1 了。

故只需要建完图后,求每个连通分量的点数和边数是否相同即可。

即我们构造的 c c c 满足 c i = a i c_i=a_i ci=ai 或者 c i = b i c_i=b_i ci=bi 即可。特殊地,对于 a i = b i a_i=b_i ai=bi 时, c i ∈ [ 1 , n ] c_i\in[1,n] ci[1,n]

代码:

#include <bits/stdc++.h>
using namespace std;constexpr int MOD = 998244353;void solve() {int n;cin >> n;vector<int> a(n), b(n);for (int i = 0; i < n; ++i) cin >> a[i], a[i] -= 1;for (int i = 0; i < n; ++i) cin >> b[i], b[i] -= 1;vector<int> p(n), points(n, 1), edges(n, 0);iota(p.begin(), p.end(), 0);auto getp = [&](int x) {int root = x;while (root != p[root]) root = p[root];while (x != p[x]) {int nx = p[x];p[x] = root;x = nx;}return root;};for (int i = 0; i < n; ++i) {int pa = getp(a[i]), pb = getp(b[i]);if (pa != pb) {points[pb] += points[pa];points[pa] = 0;edges[pb] += edges[pa];edges[pa] = 0;p[pa] = pb;} else {edges[pb] += 1;}}bool ok = true;for (int i = 0; i < n; ++i)if (i == getp(i)) {if (edges[i] != points[i]) {ok = false;break;}}if (ok) cout << "YES\n";else cout << "NO\n";
}int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int T = 1;cin >> T;while (T--) solve();return 0;
}

原问题

题意:

给定两个长度为 n n n 的数组 a , b a,b a,b 1 ≤ a i , b i ≤ n 1\leq a_i,b_i\leq n 1ai,bin。需要构造一个长度为 n n n 的数组 c c c 1 ≤ c i ≤ n 1\leq c_i\leq n 1cin。两个人 A l i c e Alice Alice B o b Bob Bob 分别拿数 n n n 次,第 i i i 次拿数时,两人从 { a i , b i , c i } \{a_i,b_i,c_i\} {ai,bi,ci} 三个数中选择两个,每次拿数 A l i c e Alice Alice 先手选择一个,拿完后 B o b Bob Bob 从剩下的两个数中选择一个。问:有多少种构造 c c c 的方式,使得有 A l i c e Alice Alice 的一种拿数选择,使得拿完后 B o b Bob Bob 的数恰好是一个 1 − n 1-n 1n 的排列。

数据范围: 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105

题解:

在子问题 2 的基础上继续考虑。

对于每个连通分量,点数和边数相同。可以看成是一棵树加上一条边。

  • 如果该连通分量中有自环边,那么这个自环边对应的点的其他边必然全是出边,该连通分量对应的方案数为 1 1 1。对于 c i c_i ci 的选择呢?自环边对应的点可以任意选择,即 n n n 个选择,其他边由于方向确定,故只有一种选择。
  • 如果该连通分量中没有自环边,那么这个多的边会和树中的一些边构成一个环,这个环上的边使得每个点的入度都已为 1 1 1,剩余的边必然都是出边,该连通分量对应的方案数为 2 2 2。对于 c i c_i ci 的选择呢?环边对应的点有两种选择,即环的两个方向。其他边由于方向确定,故只有一种选择。

代码:

#include <bits/stdc++.h>
using namespace std;constexpr int MOD = 998244353;void solve() {int n;cin >> n;vector<int> a(n), b(n);for (int i = 0; i < n; ++i) cin >> a[i], a[i] -= 1;for (int i = 0; i < n; ++i) cin >> b[i], b[i] -= 1;vector<int> p(n), points(n, 1), edges(n, 0);iota(p.begin(), p.end(), 0);auto getp = [&](int x) {int root = x;while (root != p[root]) root = p[root];while (x != p[x]) {int nx = p[x];p[x] = root;x = nx;}return root;};vector<int> self_loop(n);for (int i = 0; i < n; ++i) {int pa = getp(a[i]), pb = getp(b[i]);if (pa != pb) {points[pb] += points[pa];points[pa] = 0;edges[pb] += edges[pa] + 1;edges[pa] = 0;self_loop[pb] += self_loop[pa];self_loop[pa] = 0;p[pa] = pb;} else {edges[pb] += 1;}if (a[i] == b[i]) self_loop[pb] += 1;}bool ok = true;int ans = 1;for (int i = 0; i < n; ++i)if (i == getp(i)) {if (edges[i] != points[i]) {ok = false;break;}if (self_loop[i] == 0) ans = ans * 2 % MOD;else ans = 1ll * ans * n % MOD;}if (!ok) {cout << "0\n";return;} else {cout << ans << "\n";}}int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int T = 1;cin >> T;while (T--) solve();return 0;
}

http://www.ppmy.cn/news/173099.html

相关文章

JAVA内存深度分析报告

文章目录 理论部分&#xff1a;1.Heap Memory&#xff08;堆内存&#xff09;2.Non-heap Memory&#xff08;堆外内存&#xff09;3.Direct Memory&#xff08;直接内存&#xff09; 实验部分&#xff1a;1.Platform MXBeans API 监控快照2.MetaSpace 快照:3.Native Memory 快照…

8TB高速存储卡,6GB/s的读写速率,适合高速流盘、信号采集存储的各种应用场景

高速信号采集和回放系统&#xff0c;需要有足够高的速率把连续采集的数据存储到磁盘&#xff0c;或者把信号生成的数据从磁盘中读取出来。一般应用中&#xff0c;数据主要通过SATA、USB、RJ45&#xff08;百兆、千兆&#xff09;等多种方式传输&#xff0c;但这些速率对于一些高…

计算机内存卡插哪里,电脑内存卡在哪个位置

台式电脑内存卡&#xff1f;没有内存卡。主机里面的存东西的叫硬盘&#xff0c;就是系统在C盘&#xff0c;其他放自己资料软件的DEFG盘&#xff0c;这个分区的所有整体是叫硬盘。一个大概1.5厘米厚度&#xff0c;一只手的长度那样&#xff0c;蛮沉的。(如果你是想把手机的内存卡…

外置内存卡

8.28 安卓4.0的手机外置内存卡失去原有作用 0925 目前手机内置两个内存卡&#xff0c;只有root后才能解决内存越来越不够用才能够以牺牲性能来换取应用运行 1106 手机的内存到底需要多大 无限制&#xff0c;直到网络速度允许云使用 1117 内存卡硬件问题导致大量相关逻辑位…

内存卡修复工具有哪些?这2种强烈推荐!

案例&#xff1a;内存卡修复工具推荐&#xff1f; “朋友们帮帮忙&#xff01;不知道为什么我的内存卡出问题了&#xff0c;里面的一些重要文件也丢失了&#xff0c;有什么办法能帮我修复内存卡吗&#xff1f;大家有什么比较好的内存卡修复工具推荐吗&#xff1f;急急急&#…

内存卡数据恢复?

在平时使用内存卡的过程中时常会遇到数据丢失的问题&#xff0c;例如误删除、格式化等。内存卡上的数据丢失了也不必烦恼&#xff0c;恢复数据并没有那么复杂。今天小编详细介绍三个方法&#xff0c;教会你如何恢复内存卡丢失的数据。 内存卡数据丢失常见原因内存卡数据恢复教…

如何恢复内存卡照片?这值得一试

存储卡是一种常用的照片存储设备&#xff0c;其便携性使其应用广泛。而在使用后不可避免需要清理工作&#xff0c;当我们在电脑上删除存储卡里的照片时&#xff0c;意味着无法通过电脑正常方法找回&#xff0c;毕竟存储卡删除后是不经过回收站的。那么如果小伙伴遇到这样的事&a…

手机:运行内存,机身内存,内存卡的区分

手机包含自带部分内存和外置内存&#xff1a; RAM,ROM 是手机自带内存&#xff0c;其中RAM 是手机运行内存&#xff0c;ROM是手机机身存储。 存储卡扩展指的是最大支持的TF卡&#xff08;一般都是T-Flash 卡&#xff09;空间&#xff0c;他是手机的外置存储设备。ROM是手机的内…