Good Bye 2022: 2023 is NEAR
写在前面
比赛地址:https://codeforces.com/contest/1770。
研究表明,玩废萌 gal 将会造成老年痴呆。
但是 RJ 的 op 真好听。
upd:虽然没玩,但是皇姬 的 op 真好听。
题面里的 Koxia 和 Mahiru 是两个 v 来着,既然出题人喜欢那就扔一下链接。好久不看 v 了,令人感慨。
真绯瑠Mahiru:https://space.bilibili.com/477306079
恋诗夜Koxia:https://space.bilibili.com/690608690
A
贪心模拟。
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define LL long long
const int kN = 10010;
//=============================================================
int a[kN], b[kN], c[kN];
LL sum;
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = read(), m = read();
sum = 0;
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int i = 1; i <= m; ++ i) b[i] = read();
for (int i = 1; i <= m; ++ i) {
int min_a = 1;
for (int j = 1; j <= n; ++ j) {
if (a[j] < a[min_a]) min_a = j;
}
a[min_a] = b[i];
}
for (int i = 1; i <= n; ++ i) sum += a[i];
printf("%lld\n", sum);
}
return 0;
}
B
最小代价为 \(n+1\)。
交替排列 \(i\) 和 \(n-i+1\) 即可。
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
//=============================================================
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = read(), k = read();
for (int i = 1, j = n; i < j; ++ i, -- j) printf("%d %d ", j, i);
if (n % 2 == 1) printf("%d ", n / 2 + 1);
printf("\n");
}
return 0;
}
C
\(t\) 组数据,每组数据给定一长度为 \(n\) 的数列 \(a\),要求判断是否存在一个正整数 \(x\),满足:
对于所有 \(1\le i<j\le n\) ,有 \(\gcd(a_i + x, a_j + x) = 1\) 成立。
\(1\le t\le 100, 2\le n\le 100, 1\le a_i\le 10^{18}, \sum n\le 10^3\)。
1S,256MB。
赛时写了个小范围暴力大范围无解的东西企图卡过去真是笑死我了。
首先数列中不能有相同的数。
之后考虑一种显然的特殊情况,当 \(\{a_i\}\) 中有至少两个奇数和两个偶数时,\(x\) 取任意值时数列中都至少有两个偶数,此时无解。
换个角度,上述情况的本质是在 \(\bmod 2\) 意义下考虑了原数列。当数列中 \(0,1\) 都出现了至少两次时无解。
扩展到其他的模数 \(p\),发现也有上述规则成立。即如果原数列 \(\{a_i\}\) 合法,则对于任何质数 \(p\),原数列在 \(\bmod p\) 意义下,\(0\sim p-1\) 的出现次数不都大于等于 \(2\)。否则无论 \(x=k\times p + r (k,r\in \N, 0\le r<p)\) 取何值,总有两个数有共同的公约数 \(p\)。
由于 \(n\) 的范围较小,则可通过枚举质数 \(p\) 进行上述检测来判断数列是否合法。由鸽巢原理,当 \(p>\frac{n}{2}\) 时,上述判断结果总是合法的,则仅需枚举 \(2\sim \frac{n}{2}\) 中的质数即可。由素数定理,这样的质数约有 \(\frac{n}{\ln n}\) 个。
总复杂度为 \(O(\frac{t\times n^2}{\ln n})\) 级别。
代码里懒得写判断质数了,复杂度成 \(O(t\times n^3)\) 了,但显然不影响正确性。
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
const int kN = 110;
#define LL long long
//=============================================================
LL a[kN], num[kN];
//=============================================================
inline LL read() {
LL f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
LL gcd(LL x_, LL y_) {
return y_ ? gcd(y_, x_ % y_) : x_;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = (int) read();
for (int i = 1; i <= n; ++ i) a[i] = read();
int flag = 0;
for (int i = 1; i <= n; ++ i) {
for (int j = i + 1; j <= n; ++ j) {
if (a[i] == a[j]) {
flag = 1;
break;
}
}
}
if (flag) {
printf("NO\n");
continue;
}
for (int p = 2; 2 * p <= n; ++ p) {
flag = 1;
for (int i = 0; i < p; ++ i) num[i] = 0;
for (int i = 1; i <= n; ++ i) num[a[i] % p] ++;
for (int i = 0; i < p; ++ i) flag &= (num[i] >= 2);
if (flag) break;
}
printf("%s\n", flag ? "NO" : "YES");
}
return 0;
}
/*
2 3 4 5
3 4 5 6
3 6 9
4 7 10
5 8 11
3 9 11
4 10 12
5 11 13
4 10 14
5 11 15
7 13 17
*/
D
Koxia 和 Mahiru 两个人在玩一个游戏。规则如下:
首先给出三个长度为 \(n\) 的整数列 \(a,b,c\),满足 \(1\le a_i,b_i,c_i\le n\),之后进行 \(n\) 轮游戏。第 \(i\) 轮规则如下:
- 令可重集合 \(S = \{a_i, b_i, c_i\}\)。
- Koxia 首先可从 \(S\) 中任意删去一个数。
- Mahiru 再从剩余的元素中任意选择一个作为 \(d_i\)。
\(n\) 轮游戏结束后,如果 \(\{d_i\}\) 是一个 \(1\sim n\) 的排列,则 Koxia 胜利,否则 Mahiru 胜利。
现在共有 \(t\) 组数据,每组数据给定两个长度为 \(n\) 的整数列 \(a\) 和 \(b\),满足 \(1\le a_i,b_i\le n\)。求有多少种长度为 \(n\) 的数列 \(c\),满足 \(1\le c_i\le n\),且使得进行上述游戏时 Koxia 必胜。答案对 \(998244353\) 取模。
\(1\le t\le 2\times 10^4, 1\le n\le 10^5, 1\le a_i,b_i\le n, \sum n\le 10^5\)。
2S,256MB。
神奇妙妙模型转化题,学到许多。
先考虑 \(a,b,c\) 均确定的情况。手玩几组数据,可以发现如下结论:
- 一共只有 \(n\) 轮游戏。如果 Koxia 必胜,则每一轮游戏 Mahiru 被迫选择的数必须不同。
- 则易发现,如果在第 \(i\) 轮游戏中,Koxia 删数后剩余的两个数 \(\{x_i, y_i\}\)不同,且为了满足结论 1,其他各轮中 Mahiru 选择的数 \(d_i\) 均不同,由鸽巢原理,在其他 \(n-1\) 轮中至少会出现一次 \(x_i\) 或者 \(y_i\),则此时 Koxia 无法必胜。
- 由结论 1、2,则在每一轮游戏中,Koxia 删数后剩余的两个数必须相同,且各轮中剩余的数均不同(构成排列)。
- 则剩余的数 \(d_i\) 一定是 \(a_i,b_i\) 中的一个。对于给定的一组数列 \(a,b\),如果 \(a_i\not= b_i\),则有 \(c_i = a_i\) 或 \(c_i = b_i\)。特别地,如果 \(a_i=b_i\),则 \(c_i\) 可取 \(1\sim n\) 的任意值。
- 特别的,当无法通过删去 \(a_i,b_i\) 中的任意一个使得剩余的数成为排列时,Koxia 无法必胜。
综上,问题转化为:
- 判断能否通过删去 \(a_i,b_i\) 中的任意一个,使得剩余的数成为排列。
- 在此基础上判断每个 \(c_i\) 的取值以计算方案数。
考虑建立神奇妙妙图论模型。在 \(a_i\) 与 \(b_i\) 间连接一条无向边,得到一个 \(n\) 个节点 \(n\) 条边,可能有自环的无向图。每条边连接的两个节点的值至多只能选一个,在无向图上考虑,问题转化为:求有多少种为每条边指定方向的方案,使得所有节点的入度均为 1。
容易证明,当且仅当每个连通块的无向边数与节点数均相同时,存在对应的方案。
证明充分性:此时每个连通块都是一个基环树。一种构造方法为:先不考虑一条环边,令该环边某端点为根使之成为有根树,令有根树上所有的边都指向深度更深的点,最后将该环边指向根。存在自环时上述构造方案也成立。
证明必要性:所有节点入度均为 1,则边数肯定与节点数两相同。
则可通过 dfs 统计每个连通块的信息非常简单地判断是否有解。解决了问题 1,在此基础上考虑如何计算方案数。
在一个有贡献的图论模型上,对于每一个连通块:
- 树边只有一种指定方向的方案。
- 对于非自环的环边构成的环,有环绕方向不同的 2 种构造方案,对应着 2 种不同的 \(c\) 的取值方案。
- 对于有自环的连通块,在图上仅有一种构造方案,但由结论 4,此时自环的点对应的 \(c_i\) 有 \(n\) 种取值。
统计连通块信息时顺便统计成环情况,将每个连通块的方案数按照乘法法则相乘即得答案。
上述过程看似复杂,但仅需进行一次 \(dfs\) 即可。总复杂度 \(O(tn)\) 级别。
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define LL long long
const int kN = 1e5 + 10;
const int kM = kN << 1;
const LL mod = 998244353;
//=============================================================
int n, a[kN], b[kN];
int edgenum, v[kM], ne[kM], head[kN];
int circle_size, node_num, edge_size, dep[kN];
bool vis[kN];
LL ans;
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
void Add(int u_, int v_) {
v[++ edgenum] = v_;
ne[edgenum] = head[u_];
head[u_] = edgenum;
}
void Dfs(int u_, int fa_, int dep_) {
++ node_num;
vis[u_] = true;
dep[u_] = dep[fa_] + 1;
for (int i = head[u_]; i; i = ne[i]) {
++ edge_size;
int v_ = v[i];
if (vis[v_]) {
if (v_ == u_) circle_size = 1;
continue;
}
Dfs(v_, u_, dep_ + 1);
}
}
void Init() {
n = read();
ans = 1;
edgenum = 0;
for (int i = 1; i <= n; ++ i) head[i] = vis[i] = dep[i] = 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 <= n; ++ i) Add(a[i], b[i]), Add(b[i], a[i]);
}
//=============================================================
int main() {
int T = read();
while (T --) {
Init();
for (int i = 1; i <= n; ++ i) {
if (!vis[i]) {
edge_size = node_num = 0;
circle_size = 2;
Dfs(i, 0, 1);
if (2 * node_num != edge_size) ans = 0;
ans = 1ll * ans * (circle_size == 1 ? n : 2) % mod;
}
}
printf("%lld\n", ans);
}
return 0;
}
/*
3 4
3 5
1 2
3 5
4 5
*/
写在最后
其他题赛时没看,先不补了。
他妈的最近两周阳了以来 krkr 模拟器使用时长达到了惊人的 50 个小时,我是超级 gal 废物