AtCoder Beginner Contest 366
A - Election 2 (abc366 A)
题目大意
张票,目前投了 给高桥, 给青木。
问剩余票随便分配,是否都是一个结局。
解题思路
考虑最好情况,即剩下票全部投给当前票少的,看看能不能超过对方,会则结局会变,否则不会变。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, t, a; cin >> n >> t >> a; if (t > a) swap(t, a); int left = n - t - a; if (t + left > a) cout << "No" << '\n'; else cout << "Yes" << '\n'; return 0; }
B - Vertical Writing (abc366 B)
题目大意
给定个字符串,把这 个字符串顺时针旋转 度,输出。
由于字符串长度不一,可能会出现 st
的情况,前面的
都要替换成*
。
解题思路
手动旋转度,然后对于每一行,从右往左,一旦碰到字符,后续再碰到
时,替换成 *
即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; int m = 0; vector<string> s(n); for (auto& i : s) { cin >> i; m = max(m, (int)i.size()); } vector<string> t(m); reverse(s.begin(), s.end()); for (int i = 0; i < m; ++i) { for (auto& j : s) { if (i < j.size()) { t[i] += j[i]; } else { t[i] += ' '; } } } for (auto& i : t) { bool start = false; for (int j = i.size() - 1; j >= 0; --j) { if (i[j] != ' ') { start = true; } if (start && i[j] == ' ') { i[j] = '*'; } } } for (auto& i : t) cout << i << '\n'; return 0; }
C - Balls and Bag Query (abc366 C)
题目大意
个操作,分三种。
1 x
放入背包一个球,数字2 x
从背包拿出一个球,数字3
问背包不同数字球的个数
解题思路
用map
维护一下各个数字球的个数,当时移除该元素,询问就是
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int q; cin >> q; map<int, int> cnt; while (q--) { int op; cin >> op; if (op == 1) { int x; cin >> x; cnt[x]++; } else if (op == 2) { int x; cin >> x; cnt[x]--; if (cnt[x] == 0) cnt.erase(x); } else { cout << cnt.size() << '\n'; } } return 0; }
D - Cuboid Sum Query (abc366 D)
题目大意
三维数组,回答个询问,每个询问问一个三维区间和。
解题思路
维护一个三元前缀和,即可通过容斥原理得到一个三元区间的和。
至于如何容斥出来的,感受下二维的情况,三维就是个项的相加减,公式即在代码里,其实就是。这里就是 , 就是 ,其余字母同理。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<vector<vector<int>>> a( n + 1, vector<vector<int>>(n + 1, vector<int>(n + 1))); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { for (int k = 1; k <= n; k++) { cin >> a[i][j][k]; } } } vector<vector<vector<int>>> sum( n + 1, vector<vector<int>>(n + 1, vector<int>(n + 1))); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { for (int k = 1; k <= n; k++) { sum[i][j][k] = a[i][j][k] + sum[i - 1][j][k] + sum[i][j - 1][k] + sum[i][j][k - 1] - sum[i - 1][j - 1][k] - sum[i - 1][j][k - 1] - sum[i][j - 1][k - 1] + sum[i - 1][j - 1][k - 1]; } } } int q; cin >> q; while (q--) { int lx, rx, ly, ry, lz, rz; cin >> lx >> rx >> ly >> ry >> lz >> rz; int ans = sum[rx][ry][rz] - sum[lx - 1][ry][rz] - sum[rx][ly - 1][rz] - sum[rx][ry][lz - 1] + sum[lx - 1][ly - 1][rz] + sum[lx - 1][ry][lz - 1] + sum[rx][ly - 1][lz - 1] - sum[lx - 1][ly - 1][lz - 1]; cout << ans << '\n'; } return 0; }
E - Manhattan Multifocal Ellipse (abc366 E)
题目大意
二维平面。给定个点。
给定,问有多少个坐标 ,满足
解题思路
从求和式子可以看出是相互独立的,,我们可以分别考虑 轴的情况。
注意到点坐标范围只有 ,我们可以直接枚举 和的值,由于,可以粗略算出来的范围不会超过 。
因此,我们直接枚举每个从到 ,计算得到 的值 。而计算这个值可以或算出来,即把绝对值去掉,即。我们对排序,然后可以二分或者枚举时动态维护这个边界点,同时维护前缀和presum
、后缀和sufsum
,以及边界点前面的点数,那么 。
这样就算出了每个的 ,同理计算出每个 的 。
剩下的问题就是从里选一个 ,从 里选一个 ,有,有多少对。
这就是个经典问题,先对两个 排序,然后依次枚举 ,那么 的都是满足的,可以二分这个边界点,或者双指针维护一下得到满足条件的的数量,累计即为答案。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, d; cin >> n >> d; vector<int> x(n), y(n); for (int i = 0; i < n; ++i) cin >> x[i] >> y[i]; auto solve = [&](vector<int>& a) { sort(a.begin(), a.end()); vector<LL> dis; int up = 2e6; int it = 0; LL presum = 0, sufsum = accumulate(a.begin(), a.end(), 0ll); for (int i = -up; i <= up; ++i) { LL sum = 0; while (it < n && a[it] < i) { presum += a[it]; sufsum -= a[it]; ++it; } sum = 1ll * it * i - presum + sufsum - 1ll * (n - it) * i; dis.push_back(sum); } sort(dis.begin(), dis.end()); return dis; }; auto dis1 = solve(x); auto dis2 = solve(y); LL ans = 0; int it = dis2.size() - 1; for (auto i : dis1) { while (it >= 0 && dis2[it] + i > d) --it; ans += it + 1; } cout << ans << '\n'; return 0; }
F - Maximum Composition (abc366 F)
题目大意
给定个一次函数 。
选择 个不同的一次函数,使得 最大。
解题思路
注意到,如果允许 重复选函数的话,因为越大, 越大,因此每次肯定选,使得值最大的函数 。但这里不允许重复。
不允许重复,会产生什么问题呢?比如一个函数 ,另一个函数,如果按照上述方法,结果就是,然而反过来则是 。即函数 虽然在第一步使用,可以得到最大的 ,但后使用,可以变得更大。因此,如果最后我会选和 ,会优先使用,会后使用。
这里就出现了函数之间,选择的偏序(顺序)问题。这个偏序怎么定义呢?函数 ,如果,则有 ,我们把 分离在一左一右,得到 。
这意味着说,如果,则,即我先用,再用 。
有了这个函数使用的偏序有什么用呢?题目要使值最大,不仅要考虑选哪 个函数,还要考虑这 个函数复合的顺序。而现在我们已经有了一个最优复合顺序了,那剩下的问题就是选哪 个函数。这其实就是一个选或不选的背包问题了。
先对 按照 从小到大排序,然后设表示考虑前 个函数,已经使用了 个函数的最大函数值。转移则考虑当前函数选或不选,从和 转移即可。初始条件 。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, k; cin >> n >> k; vector<array<int, 2>> f(n); for (auto& [a, b] : f) cin >> a >> b; sort(f.begin(), f.end(), [](const array<int, 2>& a, const array<int, 2>& b) { auto& [a1, b1] = a; auto& [a2, b2] = b; return (a2 - 1) * b1 > (a1 - 1) * b2; }); vector<LL> dp(k + 1, 0); dp[0] = 1; for (int i = 0; i < n; ++i) { auto& [a, b] = f[i]; vector<LL> dp2 = dp; for (int j = 1; j <= k; ++j) { dp2[j] = max(dp2[j], a * dp[j - 1] + b); } dp.swap(dp2); } cout << dp[k] << '\n'; return 0; }
G - XOR Neighbors (abc366 G)
题目大意
给定一张图,给每个点一个点权,使得每个点的邻居的点权异或和为。给出方案,或告知不能。
解题思路
<++>
神奇的代码
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/18352899
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2020-08-10 2020百度之星程序设计大赛复赛