AtCoder Beginner Contest 306
A - Echo (abc306 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; string s; cin >> n >> s; for(auto &i : s) cout << i << i; cout << '\n'; return 0; }
B - Base 2 (abc306 b)
题目大意
给定一个从低位开始的二进制串,将其转为十进制。
解题思路
注意有位,得用 unsigned long long
。
神奇的代码
#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); unsigned long long ans = 0; unsigned long long ji = 1; for(int i = 0; i < 64; ++ i){ int x; cin >> x; if (x) ans += ji; ji <<= 1; } cout << ans << '\n'; return 0; }
C - Centers (abc306 c)
题目大意
给定一个都出现三次的数组 。
定义 表示 第二次出现在 的位置。
按照位置的升序输出 。
解题思路
按照题意求出位置后,排序即可。
神奇的代码
#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<int> pos(n, -2); for(int i = 0; i < 3 * n; ++ i){ int x; cin >> x; -- x; if (pos[x] == -2){ pos[x] = -1; }else if (pos[x] == -1) pos[x] = i; } vector<int> ans(n); iota(ans.begin(), ans.end(), 0); sort(ans.begin(), ans.end(), [&](int a, int b){ return pos[a] < pos[b]; }); for(auto &i : ans) cout << i + 1 << ' '; cout << '\n'; return 0; }
D - Poisonous Full-Course (abc306 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; array<LL, 2> dp{0, 0}; for(int i = 0; i < n; ++ i){ array<LL, 2> dp2{0, 0}; LL x, y; cin >> x >> y; if (x == 0){ dp2[0] = max(dp[0] + max(y, 0ll), dp[1] + y); dp2[1] = dp[1]; }else{ dp2[0] = dp[0]; dp2[1] = max(dp[0] + y, dp[1]); } dp.swap(dp2); } cout << *max_element(dp.begin(), dp.end()) << '\n';; return 0; }
E - Best Performances (abc306 e)
题目大意
给定一个数组,定义其价值为前大的数的和。
有 次操作,每次操作令 。
每次操作后,回答其价值。
操作是持久化的。
解题思路
用两个,分别维护前 大的和剩余的数。
每次操作则修改对应的 的值,然后比较 左边的最小值和右边的最大值,如果小于则交换一下这两个数。
每次操作最多就交换两个数,因此复杂度是
神奇的代码
#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, q; cin >> n >> k >> q; array<set<pair<int, int>>, 2> num; for(int i = 0; i < k; ++ i){ num[1].insert({0, i}); } for(int i = k; i < n; ++ i){ num[0].insert({0, i}); } LL ans = 0; vector<int> a(n, 0); while(q--){ int x, y; cin >> x >> y; -- x; auto token = make_pair(a[x], x); if (num[1].count(token)){ num[1].erase(token); num[1].insert({y, x}); ans += y - a[x]; }else{ num[0].erase(token); num[0].insert({y, x}); } a[x] = y; while(num[1].size() < k){ auto l = *num[0].rbegin(); ans += l.first; num[1].insert(num[0].extract(l)); } while(num[1].size() > k){ auto r = *num[1].begin(); ans -= r.first; num[0].insert(num[1].extract(r)); } while(!num[0].empty() && num[0].rbegin() -> first > num[1].begin() -> first){ auto l = *num[0].rbegin(), r = *num[1].begin(); ans += l.first - r.first; num[1].insert(num[0].extract(l)); num[0].insert(num[1].extract(r)); } cout << ans << '\n'; } return 0; }
F - Merge Sets (abc306 f)
题目大意
对于两个交集为空的集合,定义为, 中每个元素在 的位置
的和。的位置
即为将其所有数升序排序后的下标(从开始)。
现给定俩俩交集均为空的个集合 ,求。
解题思路
由于,分析答案的贡献,发现可以转换成求每个数的贡献。因此我们的视角从枚举集合转移到枚举每个数。
对于第个集合的升序排列的第 个数 (此处下标均从开始,同代码一致),我们考虑它会对答案贡献多少。
注意 ,这意味着我们只有选择 的集合 时, 才有贡献。
然后考虑选择了后, 的是排第几位。假设 中有 个小于 的,那么此时 的贡献是 。然后将所有的 的这些贡献累加,就是 对答案的贡献。
直接累加的复杂度是 ,很显然不能这样算。
我们分析这个贡献式子,它可以拆成三部份 。第一项和最后一项会贡献 次(注意从第集合算起)。
而第二项的, 我们可以把个数全部丢到数组里排个序,假设排在第 位,那么 的值就是在数组中,所有位置,且位于集合下标 的数的个数。
显然这是一个二维偏序问题,同abc283f一样。我们可以用树状数组维护关于集合下标的数的个数,然后从小到大遍历数组,依次把每个数加进树状数组中。这样查询一下后缀和,就是。进而 的贡献就是 ,对于每个数依次这么计算即可。
时间复杂度为
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; template <typename T> class fenwick { public: vector<T> fenw; int n; fenwick(int _n) : n(_n) { fenw.resize(n); } void modify(int x, T v) { while (x < n) { fenw[x] += v; x |= (x + 1); } } T get(int x) { T v{}; while (x >= 0) { v += fenw[x]; x = (x & (x + 1)) - 1; } return v; } }; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m; cin >> n >> m; vector<array<int, 2>> a(n * m); int pos = 0; for(int i = 0; i < n; ++ i){ for(int j = 0; j < m; ++ j){ cin >> a[pos][0]; a[pos][1] = i; pos ++; } } sort(a.begin(), a.end(), [](const auto &x, const auto &y){ return x[0] < y[0]; }); fenwick<int> cnt(n); LL ans = 0; int sum = 0; for(int i = 0; i < n * m; ++ i){ int belong = a[i][1]; int pre = cnt.get(belong - 1); int cur = cnt.get(belong); ans += sum - cur + 1ll * (n - belong - 1) * (cur - pre + 1); cnt.modify(belong, 1); sum ++; } cout << ans << '\n'; return 0; }
G - Return to 1 (abc306 g)
题目大意
给定一张有向图,问能否从号点出发,经过 条边后,回到 号点。
解题思路
<++>
神奇的代码
Ex - Balance Scale (abc306 h)
题目大意
给定一个数组。
进行 次操作,每次操作从 中取两个数,然后将它们的大小结果 之一加入字符串 的最后。
问字符串可能的情况数量。
解题思路
<++>
神奇的代码
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/17488352.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步