Codeforces Global Round 22 A-E
A
题解
知识点:贪心。
显然交错释放最好。
若两类数量不一样,那么较少的一组的一定都可以双倍,剩下的另一组就放进一个优先队列,从大到小和少的一组匹配可以双倍,剩下的直接加。
如果两类数量一样,那一定有一个不能被双倍。用上面的方法后,减去一个两组的最小值即可。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int a[100007], b[100007]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) cin >> a[i]; for (int i = 1;i <= n;i++) cin >> b[i]; int cnt1 = 0; for (int i = 1;i <= n;i++) if (a[i]) cnt1++; ll sum = 0; bool typ = cnt1 <= n - cnt1; int cnt = min(cnt1, n - cnt1); priority_queue<int> pq; for (int i = 1;i <= n;i++) { if (a[i] == typ) sum += 2 * b[i]; else pq.push(b[i]); } for (int i = 1;i <= cnt;i++) { sum += pq.top() * 2; pq.pop(); } while (pq.size()) { sum += pq.top(); pq.pop(); } if (n - cnt == cnt) sum -= *min_element(b + 1, b + n + 1); cout << sum << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
B
题解
知识点:枚举,前缀和,贪心。
注意,给出的 个前缀和,能确定后 个数,其他数是可以自由分配的,那么现在只需要让倒数第 个数最小即可。
显然 最小值为 ,即把 的值平均分配给 个位置,就能保证 最小。
接下来,通过这个最小的 检验后面 项是否合法即可。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int s[100007]; bool solve() { int n, k; cin >> n >> k; for (int i = 1;i <= k;i++) cin >> s[i]; int cur; if (s[1] <= 0) cur = s[1] / (n - k + 1); else cur = (s[1] + n - k) / (n - k + 1); for (int i = 2;i <= k;i++) { if (s[i] - s[i - 1] < cur) return false; cur = s[i] - s[i - 1]; } cout << "YES" << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << "NO" << '\n'; } return 0; }
C
题解
方法一
知识点:博弈论。
考虑奇数数量 模 的四种情况。
- ,此时A总是能拿到偶数个奇数,必赢。
- ,若 为偶数,那A可以不拿多出来的那个奇数,从而必赢;否则,一定会拿多一个奇数,必输。
- ,此时A总是能拿到奇数个奇数,必输。
- ,此时A总是能拿到偶数个奇数,必赢。
时间复杂度
空间复杂度
方法二
知识点:博弈论,线性dp,记忆化搜索。
设 当前为A/B(0/1)的回合,A手里的奇偶性,偶数剩 ,奇数剩 时当前回合人的输赢。终止局面:A的回合且A的和为偶数时为A赢,B的回合且A的和为奇数时为B赢,其他情况均为当前回合的人输。
这样每个回合,只要下一回合奇数偶数中有一种情况是下一个回合的人输,那自己一定是赢的,每次或转移即可。
还有种设法是只看 的输赢,但写起来不方便,因为上面这种写法可以同一或转移,这种写法A是或转移,B是且转移。因为B只要有一种能让A输就会走,应该是且,那就需要多一个判断回合。
时间复杂度
空间复杂度
代码
方法一
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { int n; cin >> n; int cnt = 0; for (int i = 1;i <= n;i++) { int x; cin >> x; if (x & 1) cnt++; } if (cnt % 4 == 0 || cnt % 4 == 3) cout << "Alice" << '\n'; else if (cnt % 4 == 1) cout << (n & 1 ? "Bob" : "Alice") << '\n'; else if (cnt % 4 == 2) cout << "Bob" << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
方法二
#include <bits/stdc++.h> #define ll long long using namespace std; int dp[2][2][107][107];//A/B(0/1)的回合,A手里的奇偶性,偶数剩多少,奇数剩多少 bool dfs(bool turn, bool st, int cnt0, int cnt1) { if (~dp[turn][st][cnt0][cnt1]) return dp[turn][st][cnt0][cnt1]; if (!cnt0 && !cnt1) return !st ^ turn; bool ans = 0; if (cnt0) ans |= !dfs(!turn, st, cnt0 - 1, cnt1); if (cnt1) ans |= !dfs(!turn, st ^ !turn, cnt0, cnt1 - 1); return dp[turn][st][cnt0][cnt1] = ans; } bool solve() { memset(dp, -1, sizeof(dp)); int n; cin >> n; int cnt0 = 0, cnt1 = 0; for (int i = 1;i <= n;i++) { int x; cin >> x; if (x & 1) cnt1++; else cnt0++; } cout << (dfs(0, 0, cnt0, cnt1) ? "Alice" : "Bob") << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
D
题解
知识点:构造。
首先解决 。注意到, 的情况只出现在 的情况。而 是个排列,即每个数字出现且仅出现一次,因此 的个数,说明 的数有多少个,即 的大小。
随后处理排列顺序。观察题意可以得到, 可以得到 一定是 的前驱。
若多个数字的前驱相同,那么这些同前驱的数字只有其中一个可以作为其他数字的前驱,且在原排列 中一定出现在同前驱数字的最后,否则会互相干扰,因此不存在这样的 。
如果 时,代表 前面没有别的数字,不难发现 和 只会有且只有一种出现在 中。
因此,可以先用一个桶把 相同的 存在一起,表示这些 的前驱相同。然后从 出发,先找到一个桶里有后继的数,把这个数放在桶的最后,再从头依次输出即可,接下来到最后一个数字,即有后继的数字,桶中重复上述操作即可。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int b[100007]; vector<int> v[100007]; bool solve() { int n; cin >> n; v[0].clear(); v[n + 1].clear(); for (int i = 1;i <= n;i++) cin >> b[i], v[i].clear(); int k = 0; for (int i = 1;i <= n;i++) if (i < b[i]) k++; for (int i = 1;i <= n;i++) v[b[i]].push_back(i); cout << k << '\n'; int cur = 0; if (v[n + 1].size()) cur = n + 1; int cnt = 0; while (cnt < n) { for (auto &i : v[cur]) { if (v[i].size()) { swap(i, v[cur].back()); break; } } for (auto i : v[cur]) cout << i << ' '; cnt += v[cur].size(); cur = v[cur].back(); } cout << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
E
题解
知识点:排列组合,双指针。
可以放置隔板划分连续区间,考虑区间 的三种情况:
- 全是 ,形如
00000000
,那么任意放置隔板都能产生回文,所以生成 种方案。 - 两侧都有 但中间两侧有数字挡着,形如
000数字..任意..数字0000
,那么考虑处理出两侧隔板方案。设左右侧分别有 个 ,且两侧需要放相同数量的隔板,因此方案数为 。 - 两侧至少有一侧没有 ,形如
00数字..任意..数字
,这时候没有 可以直接处理,那就要处理出从两侧出发和相同的一段。假设和相同时,左段右端点是 ,右段左端点是 ,若 ,说明两端重合了不能形成回文;否则,可以把这一段整体等效 ,参与下一次区间的处理。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; const int mod = 998244353; int fact[100007], invf[100007]; int a[100007]; int qpow(int a, int k) { int ans = 1; while (k) { if (k & 1) ans = 1LL * ans * a % mod; k >>= 1; a = 1LL * a * a % mod; } return ans; } void init(int n) { fact[0] = 1; for (int i = 1;i <= n;i++) fact[i] = 1LL * fact[i - 1] * i % mod; invf[n] = qpow(fact[n], mod - 2); for (int i = n;i >= 1;i--) invf[i - 1] = 1LL * invf[i] * i % mod; } int C(int n, int m) { return 1LL * fact[n] * invf[m] % mod * invf[n - m] % mod; } int f(int i, int j) { int l = i, r = j; while (l <= j && !a[l]) l++; while (r >= i && !a[r]) r--; if (l == j + 1) return qpow(2, j - i); if (l != i && r != j) { int ans = 0, x = l - i, y = j - r; for (int i = 0;i <= min(x, y);i++) ans = (ans + 1LL * C(x, i) * C(y, i)) % mod; return 1LL * ans * f(l, r) % mod; } ll ls = a[l], rs = a[r]; while (l < r && ls != rs) { if (ls < rs) ls += a[++l]; else rs += a[--r]; } if (l >= r) return 1; a[l] = a[r] = 0; return f(l, r); } bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) cin >> a[i]; cout << f(1, n) << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); init(100001); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16806166.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧