Codeforces Round #822 (Div. 2) A-F
A
题解
知识点:贪心。
注意到任意三根木棍的相等最优解是最长减最小,因此从小到大排序,三个三个取,取最小值。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; ll a[307]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) cin >> a[i]; sort(a + 1, a + n + 1); ll ans = a[3] - a[1]; for (int i = 3;i <= n;i++) { ans = min(ans, a[i] - a[i - 2]); } cout << ans << '\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; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) { for (int j = 1;j <= i;j++) { if (j == 1 || j == i) cout << 1 << ' '; else cout << 0 << ' '; } 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; }
C
题解
知识点:贪心,数学。
从小到大,把每一个要删除的数当作 枚举倍数,如果是要删除的数花费一次 删掉,如果已经删过则无视,如果不是要删除的数则停止换下一个 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int vis[1000007]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) { char c; cin >> c; vis[i] = c == '1'; } ll sum = 0; for (int i = 1;i <= n;i++) { if (vis[i] == 1) continue; for (int j = i;j <= n;j += i) { if (vis[j] == 1) break; if (vis[j] == 0) { vis[j] = 2; sum += i; } } } 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; }
D
题解
知识点:贪心,枚举。
先选择一个方向直走,比如先走左侧,走到不能再走为止,把尽头的生命值 记录下。
此时考虑回头,但显然在左侧尽头回头不是一定最优的,应该在走左侧过程中生命值和最大处回头才是最优的,因为这样在走右侧时可以走最多的路,因此在走左侧的过程中也要记录左侧的生命和最大值 。
同理从 回头走右侧时,也是走到尽头记录右侧最大生命值 和尽头生命值 。此时从 回头走左侧,应该直接从上一次的左侧尽头位置 继续走。
如此来回往复,直到两侧不能继续走或者到达两端为止。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int a[200007]; bool solve() { int n, k; cin >> n >> k; for (int i = 1;i <= n;i++) cin >> a[i]; int i = k - 1, j = k + 1; ll lmx = 0, lnw = 0, rmx = 0, rnw = 0; while (1 <= i && j <= n) { bool ok = false; while (1 <= i) { if (a[k] + lnw + rmx + a[i] < 0) break; ok = true; lnw += a[i--]; lmx = max(lmx, lnw); } while (j <= n) { if (a[k] + lmx + rnw + a[j] < 0) break; ok = true; rnw += a[j++]; rmx = max(rmx, rnw); } if (!ok) break; } if (i == 0 || j == n + 1) cout << "YES" << '\n'; else cout << "NO" << '\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
题解
知识点:构造,数学。
注意到,
猜测一行元素具有线性关系,设 行线性系数为 , 行线性系数为 ,于是有:
根据定理:当 时,若 ,则 。
于是有:
因此,只要每行之间的线性系数在 意义下不同余,且在 处经过 即可。
显然, 时即能保证互不同余,可以当作系数,因此有公式 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int a[357][357], b[357]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) cin >> b[i]; for (int i = 1;i <= n;i++) { for (int j = 1;j <= n;j++) { a[i][j] = ((i * (j - i) + b[i]) % n + n) % n; } } for (int i = 1;i <= n;i++) { for (int j = 1;j <= n;j++) { cout << a[i][j] << ' '; } 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; }
F
题解
知识点:记忆化搜索,线性dp,数学,位运算。
先是一个结论:定义函数 表示 二进制位 的个数的奇偶性(奇数返回 ,偶数返回 ),那么 。
证明非常简单:
- 由于 的生成方法是每次都从原来的一份取反得到 放到 末尾,所以第 次扩展后 的编号一定是 。
- 对于第 次新生成的 中的每一位编号 ,满足 ,因为编号 的第 位之前一定是 ,所以这次变换实际上是将编号 的第 位变为 作为编号 。
- 显然,所有数字都是从编号 开始数次变换得到的,每次变换都会将编号的一位 变为 ,因此我们记录二进制 的数量就能得知这个编号从 变换了多少次。
- ,所以编号 有偶数个 就是变了偶数次,故 ,否则 。即 。
有了这个结论,我们就可以对问题进行量化。记原问题答案为 ,有 。
当 时,显然有 。
当 为奇数时,先对末尾判断再对 讨论(偶数讨论方便一点),有 。
当 为偶数时:
-
为偶数,有如下关系:
因为偶数末尾总是 ,加 不会影响其余的二进制位,所以 的数量明确加 ,奇偶性一定同时改变。
因为偶数末尾总是 ,删去这个 后,数字奇偶性不变。
那么有如下公式:
-
为奇数,有如下关系:
以及,
证明同上。
至此,我们就可以通过记忆化搜索进行求解了。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool check(ll a, ll b) { return __builtin_parityll(a) != __builtin_parityll(b); } map<pair<ll, ll>, ll> mp; ll f(ll n, ll m) { if (m == 0) return 0; if (mp.count({ n,m })) return mp[{n, m}]; if (m & 1) return mp[{n, m}] = f(n, m - 1) + check(m - 1, n + m - 1); if (n & 1) return mp[{n, m}] = m - f(n / 2, m / 2) - f((n + 1) / 2, m / 2); else return mp[{n, m}] = 2 * f(n / 2, m / 2); } bool solve() { ll n, m; cin >> n >> m; cout << f(n, m) << '\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; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16758490.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧