Educational Codeforces Round 141
写在前面
比赛地址:https://codeforces.com/contest/1783。
CF 车队翻车咯,本来上大分,喜提 skipped
A
如果所有数均相等则无解。
否则先降序排序,交替输出 和 即可。
复制复制//By:Luckyblock /* */ #include <cstdio> #include <cctype> #include <algorithm> //============================================================= int a[1100]; //============================================================= 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(); for (int i = 1; i <= n; ++ i) a[i] = read(); std::sort(a + 1, a + n + 1); if (a[n] == a[1]) { printf("NO\n"); continue; } printf("YES\n"); for (int i = 1, j = n; i <= j; ++ i, -- j) { if (i == j) printf("%d ", a[i]); else printf("%d %d ", a[j], a[i]); } printf("\n"); } return 0; }
B
尝试达到上界。
蛇形矩阵,交替输出 和 即可。
//By:Luckyblock /* */ #include <cstdio> #include <cctype> #include <algorithm> //============================================================= int a[51 * 51], b[51][51]; //============================================================= 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() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { int n = read(), p = 0; for (int i = 1, j = n * n; i <= j; ++ i, -- j) { a[++ p] = i, a[++ p] = j; if (i == j) -- p; } for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) { b[i][j] = 0; } } b[1][1] = a[1]; for (int i = 1, j = 1, tot = 1; tot < n * n; ) { while(++ j <= n && !b[i][j]) b[i][j] = a[++ tot]; -- j; while(++ i <= n && !b[i][j]) b[i][j] = a[++ tot]; -- i; while(-- j > 0 && !b[i][j]) b[i][j] = a[++ tot]; ++ j; while(-- i > 0 && !b[i][j]) b[i][j] = a[++ tot]; ++ i; } for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) { printf("%d ", b[i][j]); } printf("\n"); } } return 0; }
C
显然获胜场数越多越好,最后的胜利场数一定是最大值,先贪心地求出这个最大值 。
再考虑其他人的获胜场数,仅需关注有多少人获胜场数大于 即可。编号 的人获胜场数一定不大于 ,编号 的人获胜场数一定大于 ,是否选择战胜他们并不会影响排名。仅需考虑是否战胜了编号 的人,回退贪心时选择的代价最大的元素并尝试选择 即可。
赛时没考虑清楚写的相当麻烦。
//By:Luckyblock /* */ #include <vector> #include <cstdio> #include <cctype> #include <algorithm> const int kN = 5e5 + 10; //============================================================= int n, m, a[kN], b[kN]; //============================================================= 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() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { n = read(), m = read(); int sum = 0; for (int i = 1; i <= n; ++ i) a[i] = b[i] = read(); std::sort(b + 1, b + n + 1); for (int i = 1; i <= n; ++ i) { if (b[i] > m) break; m -= b[i]; sum ++; } if (sum == n) { printf("1\n"); continue; } printf("%d\n", m + b[sum] - a[sum + 1] >= 0 ? n - sum: n - sum + 1); } return 0; }
D
给定一长度为 的数列 ,要求进行 次操作,第 次操作有两种选择:
- 令 ,。
- 令 ,。
求完成所有操作后,数列 的形态的种类数。
,。
2S,512MB。
设 次操作后, 的形态有 种,考虑第 次操作的影响。发现并不会影响 和 的值,它们的形态并不会影响本次操作对 形态种类数,我们仅需关注 和 的值即可。更近一步地,由于 的值在操作前已经确定, 的值在操作中不会改变,考虑枚举 的值,我们仅需考虑 的值即可。
记 表示进行到第 次操作, 的 的形态数。转移时考虑枚举 的值 :
- 若 ,两种操作等价,有:
- 若 ,则有:
答案即 。
总复杂度 级别, 为值域。
注意 , 可能为负数,注意添加增量避免数组下标为 0。
//By:Luckyblock /* */ #include <cstdio> #include <cctype> #include <algorithm> const int mod = 998244353; const int kN = 3e2 + 10; const int kM = 3e5; const int zero = 1e5; //============================================================= int n, a[kN], ans, f[kN][kM]; //============================================================= 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() { // freopen("1.txt", "r", stdin); n = read(); for (int i = 1; i <= n; ++ i) a[i] = read(); f[0][a[2] + zero] = 1; for (int i = 1; i <= n - 2; ++ i) { int val = a[i + 2]; for (int j = 0; j < kM; ++ j) { int nval1 = val + j, nval2 = val - (j - zero) + zero; f[i][nval1] = (f[i][nval1] + f[i - 1][j]) % mod; if (j != zero) f[i][nval2] = (f[i][nval2] + f[i - 1][j]) % mod; } } for (int i = 0; i < kM; ++ i) ans = (ans + f[n - 2][i]) % mod; printf("%lld\n", ans); return 0; }
E
AB 两人在玩一个游戏,游戏中共有 个怪物。对于每一个怪物,A,B 可以轮流攻击 次,A 先出手,两人的攻击互不影响。A 的第 次攻击才能击败第 个怪物,B 的第 次攻击才能击败第 个怪物。求所有的整数 ,使得所有怪物均是被 A 击败的。
组数据,每组数据给定两长度为 的数组 ,代表上述游戏的参数,求所有的整数 。
,,,。
2S,256MB。
没做亏死……虽然就算做了也会翻车,乐
对于一个固定的 ,第 个怪物被 A 击败的条件为:,考虑反面,被 B 击败的条件为 ,则 之间至少有一个整数,则有 ,且 之间至少有一个 的倍数。
考虑枚举 ,检查 的倍数是否位于某个区间 中即可判断是否有解。差分标记区间即可。
复杂度是调和级数, 级别。
//By:Luckyblock /* */ #include <cstdio> #include <cctype> #include <vector> #include <algorithm> const int kN = 2e5 + 10; //============================================================= int n, a[kN], b[kN], d[kN]; //============================================================= 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() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { n = read(); for (int i = 1; i <= n; ++ i) d[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) { if (b[i] <= a[i]) { ++ d[b[i]], -- d[a[i]]; } } for (int i = 1; i <= n; ++ i) d[i] += d[i - 1]; std::vector <int> ans; for (int k = 1; k <= n; ++ k) { bool flag = 1; for (int i = 1; i * k <= n; ++ i) { if (d[i * k]) { flag = 0; break; } } if (flag) ans.push_back(k); } printf("%d\n", ans.size()); for (int i = 0, sz = ans.size(); i < sz; ++ i) printf("%d ", ans[i]); printf("\n"); } return 0; }
F
给定两个长度为 的排列 ,可以进行任意次操作。每次操作可选择整数 ,设 分别满足 ,交换 和 ,。
要求构造一种操作数 的方案,使得使两个排列均有序,且最小化操作次数。
,。
写了个贪心调了一天没调出来最后发现假了,心力交瘁,改天再说、、、
首先,显然至多进行 次操作,因为每次操作都至少会让两个排列中的某个数交换到有序位置。
考虑排列元素交换的套路(如 CF1768D),对于一个排列,建图令 ,使图中每一个连通块有序的代价为块的大小 -1,即每个连通块中都有一个不需要操作的 ,其他的 都需要被操作。总代价为点数-连通块数。
再考虑两个排列的情况,记两个排列对应的图分别为 。考虑问题反面,最大化不需要被操作的位置数。放到图上看,如果一个位置 可以不被操作,那么 中 所在的连通块中的其他位置都需要被操作。
考虑对这个问题再建立图论模型,记 中 所在的连通块分别为 ,令 ,可以发现这是一张二分图,每个原排列中的位置对应二分图的一条边。最大化不需要被操作的位置数,等价于求该二分图的最大匹配数,最小操作次数即 最大匹配数。
注意二分图中可能有重边,而最大匹配时仅会选择重边中的一条,输出方案时应注意判断。
总复杂度 级别。
//By:Luckyblock /* */ #include <cstdio> #include <cctype> #include <vector> #include <algorithm> const int kN = 3e3 + 10; const int kM = kN; //============================================================= int n, ans, a[kN], b[kN]; int numa, numb, bela[kN], belb[kN]; bool visa[kN], visb[kN]; int edgenum, head[kN], v[kM], ne[kM]; int match[kN]; bool vis[kN], visans[kN]; //============================================================= 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 Dfsa(int u_) { if (visa[u_]) return ; visa[u_] = 1, bela[u_] = numa; Dfsa(a[u_]); } void Dfsb(int u_) { if (visb[u_]) return ; visb[u_] = 1, belb[u_] = numb; Dfsb(b[u_]); } void Add(int u_, int v_) { v[++ edgenum] = v_; ne[edgenum] = head[u_]; head[u_] = edgenum; } bool Dfs(int u_) { for (int i = head[u_]; i; i = ne[i]) { int v_ = v[i]; if (vis[v_]) continue; vis[v_] = 1; if (!match[v_] || Dfs(match[v_])) { match[v_] = u_; return 1; } } return 0; } //============================================================= int main() { // freopen("1.txt", "r", stdin); n = read(); for (int i = 1; i <= n; ++ i) a[i] = read(); for (int j = 1; j <= n; ++ j) b[j] = read(); for (int i = 1; i <= n; ++ i) { if (!visa[i]) ++ numa, Dfsa(i); if (!visb[i]) ++ numb, Dfsb(i); } for (int i = 1; i <= n; ++ i) Add(bela[i], belb[i]); for (int i = 1; i <= numa; ++ i) { for (int j = 1; j <= numb; ++ j) vis[j] = 0; if (Dfs(i)) ++ ans; } printf("%d\n", n - ans); for (int i = 1; i <= numa; ++ i) { for (int j = head[i]; j; j = ne[j]) { if (match[v[j]] == i) { visans[j] = 1; match[v[j]] = 0; } } } for (int i = 1; i <= n; ++ i) { if (!visans[i]) printf("%d ", i); } return 0; }
G
很有意思的线段树分治。
但是这个分析太过牛逼,虽然看懂了但不太明白思路怎么来的。
牛逼。
先不写了就,什么时候复习线段树分治拉出来写一下。
写在最后
- 开车需谨慎。
- 想清楚再写,别急。
- 考虑影响因素,影响因素相对较少且无后效性考虑 dp。
- 考虑反面。
- 模型转换。
- 仁王真好玩,在线求斧哥送我仁王 2 玩。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】