AtCoder Beginner Contest 346
A - Adjacent Product (abc346 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; cin >> n; int la = 0; cin >> la; for (int i = 1; i < n; i++) { int a; cin >> a; cout << la * a << '\n'; la = a; } return 0; }
B - Piano (abc346 B)
题目大意
给定一个由wbwbwwbwbwbw
无限拼接的字符串。
给定,问是否由一个子串,其有 个 w
, 个 b
解题思路
考虑枚举子串的起点,其实只有种情况。
枚举起点后,统计其后的个字符,看看是否满足上述需求即可。
时间复杂度是
神奇的代码
#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); string s = "wbwbwwbwbwbw"; int w, b; cin >> w >> b; int len = w + b; bool ok = false; for (int i = 0; i < s.size(); ++i) { map<char, int> cc; for (int j = i, cnt = 0; cnt < len; j = (j + 1) % s.size(), ++cnt) { cc[s[j]]++; } if (cc['w'] == w && cc['b'] == b) { ok = true; break; } } if (ok) cout << "Yes" << endl; else cout << "No" << endl; return 0; }
C - Σ (abc346 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, k; cin >> n >> k; vector<int> a(n); for (auto& i : a) cin >> i; LL ans = 1ll * k * (k + 1) / 2; for (auto& i : set<int>(a.begin(), a.end())) { if (i <= k) { ans -= i; } } cout << ans << '\n'; return 0; }
D - Gomamayo Sequence (abc346 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; string s; cin >> n >> s; vector<int> c(n); for (auto& i : c) cin >> i; vector<array<LL, 2>> pre(n + 1), suff(n + 1); const string expect = "01"; for (int i = 0; i < n; ++i) { pre[i][0] = (s[i] == expect[i & 1] ? c[i] : 0) + (i > 0 ? pre[i - 1][0] : 0); pre[i][1] = (s[i] == expect[~i & 1] ? c[i] : 0) + (i > 0 ? pre[i - 1][1] : 0); } for (int i = n - 1; i >= 0; --i) { suff[i][0] = (s[i] == expect[i & 1] ? c[i] : 0) + (i < n - 1 ? suff[i + 1][0] : 0); suff[i][1] = (s[i] == expect[~i & 1] ? c[i] : 0) + (i < n - 1 ? suff[i + 1][1] : 0); } LL ans = 1e18 + 7; ; for (int i = 0; i < n - 1; ++i) { ans = min({ans, pre[i][0] + suff[i + 1][1], pre[i][1] + suff[i + 1][0]}); } cout << ans << '\n'; return 0; }
E - Paint (abc346 E)
题目大意
的平面,格子初始全为颜色 。
依次进行 次操作,每次操作将某一行或某一列的格子涂成颜色 。
问最后各个颜色的格子数量。
解题思路
朴素的想法,最后统计每块格子的颜色,时间复杂度避免不了为为。
考虑到每次操作都是对一行或一列涂色,其涂的格子数是已知的,所以可以直接累计结果。
但这样的问题是,后面的操作会影响到前面的结果,颜色会覆盖,导致先前涂的颜色数量可能会减少。
注意到后面的操作会覆盖前面的操作,如果我们对操作反过来考虑,先考虑最后一次操作,再考虑前一个操作,那么后考虑的操作不会影响先考虑的操作,就不会出现上面的先前涂的颜色会减少的问题。
因此我们对操作倒过来考虑,每次操作所涂的格子数。格子数的求法比较简单,比如一次操作对某一行涂色,其涂得格子数为已经涂过的列操作数。维护一下已经涂过的列和行数量即可。当然还要维护某一列和某一行是否被涂过,后涂的操作是无效的。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int up = 2e5 + 8; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int h, w, m; cin >> h >> w >> m; vector<array<int, 3>> op(m); for (auto& [t, a, x] : op) { cin >> t >> a >> x; --a; } vector<LL> cnt(up, 0); vector<int> used_row(h, 0); vector<int> used_col(w, 0); int row = h, col = w; reverse(op.begin(), op.end()); for (auto& [t, a, x] : op) { if (t == 1) { if (used_row[a]) continue; used_row[a] = 1; --row; cnt[x] += col; } else { if (used_col[a]) continue; used_col[a] = 1; --col; cnt[x] += row; } } cnt[0] += 1ll * h * w - accumulate(cnt.begin(), cnt.end(), 0ll); vector<pair<int, LL>> ans; for (int i = 0; i < up; ++i) { if (cnt[i] != 0) ans.emplace_back(i, cnt[i]); } cout << ans.size() << '\n'; for (auto& [i, c] : ans) { cout << i << " " << c << '\n'; } return 0; }
F - SSttrriinngg in StringString (abc346 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); LL n; string s, t; cin >> n >> s >> t; vector<vector<int>> pos(26); for (int i = 0; i < s.size(); ++i) { pos[s[i] - 'a'].push_back(i); } LL l = 0, r = 1e18; auto check = [&](LL x) { if (x == 0) return true; LL sum = 0; int cur = 0; int it = 0; LL left = x; while (sum <= n && cur < t.size()) { int c = t[cur] - 'a'; if (pos[c].size() == 0) return false; if (it == 0) { int cnt = pos[c].size(); LL cost = left / cnt; sum += cost; left -= cost * cnt; LL mod = left % cnt; if (mod != 0) { sum += 1; it = (pos[c][mod - 1] + 1) % s.size(); left -= mod; } else { it = (pos[c].back() + 1) % s.size(); } } else { int st = lower_bound(pos[c].begin(), pos[c].end(), it) - pos[c].begin(); int cnt = pos[c].size() - st; if (left > cnt) { left -= cnt; it = 0; } else { it = (pos[c][st + left - 1] + 1) % s.size(); left = 0; } } if (left == 0) { ++cur; left = x; } } return sum <= n; }; while (l + 1 < r) { LL mid = (l + r) >> 1; if (check(mid)) l = mid; else r = mid; } cout << l << '\n'; return 0; }
G - Alone (abc346 G)
题目大意
给定个数。问 的数量,满足中有数字仅出现一次。
解题思路
几个常规思路,比如枚举, 看有几个符合条件的,或者分治之类的,都不行,棘手在于有数字仅出现一次
这一条件不好处理。
那就尝试枚举 仅出现一次的数字
,记该数字的左右两边最近的数字 的位置,那么所有满足 条件的区间都是满足题意的区间,个数即为两个可行区间大小的乘积。
所有的这样的区间个数加起来,但不是答案,会有算重的。比如一个区间 包含了两个仅出现一次的数字
,那么这个区间会算成两份。关键是如何去重。
如何理解去重,设想一个二维平面,横坐标是,纵坐标是 ,那么上述的每一个 就对应一个矩形,矩形之间可能有交。答案就是矩形的面积。
从这个角度理解的话,解法就呼之欲出了:扫描线扫一遍就是答案了。
代码实现里,因为扫描线是枚举一维(比如),用线段树维护另一维( 的可行位置个数),而这里最大只有 ,因此可以直接枚举,算一层一层的结果,而不必像平时扫描线里的离散化之类的。枚举每个 之后,更新 的可行区域。
到最后其实就是枚举 ,看有几个符合条件的这样的思路,只是中间怎么维护比较难思考,理解矩形面积+扫描线后就能更好想到做法了。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int N = 2e5 + 8; class segment { #define lson (root << 1) #define rson (root << 1 | 1) public: LL minn[N << 2]; LL cnt[N << 2]; LL lazy[N << 2]; void build(int root, int l, int r) { if (l == r) { minn[root] = 0; lazy[root] = 0; cnt[root] = 1; return; } int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r); lazy[root] = 0; } void pushdown(int root) { if (lazy[root]) { minn[lson] += lazy[root]; minn[rson] += lazy[root]; lazy[lson] += lazy[root]; lazy[rson] += lazy[root]; lazy[root] = 0; } } void update(int root, int l, int r, int L, int R, LL val) { if (L <= l && r <= R) { minn[root] += val; lazy[root] += val; return; } pushdown(root); int mid = (l + r) >> 1; if (L <= mid) update(lson, l, mid, L, R, val); if (R > mid) update(rson, mid + 1, r, L, R, val); minn[root] = min(minn[lson], minn[rson]); cnt[root] = (minn[lson] == minn[root] ? cnt[lson] : 0) + (minn[rson] == minn[root] ? cnt[rson] : 0); } pair<int, int> query(int root, int l, int r, int L, int R) { if (L <= l && r <= R) { return {minn[root], cnt[root]}; } pushdown(root); int mid = (l + r) >> 1; pair<int, int> resl = {INT_MAX, 0}, resr = {INT_MAX, 0}; if (L <= mid) resl = query(lson, l, mid, L, R); if (R > mid) resr = query(rson, mid + 1, r, L, R); pair<int, int> res; res.first = min(resl.first, resr.first); res.second = (resl.first == res.first ? resl.second : 0) + (resr.first == res.first ? resr.second : 0); return res; } } sg; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n); for (auto& x : a) { cin >> x; --x; } sg.build(1, 1, n); vector<vector<int>> pos(n, vector<int>(1, 0)); LL ans = 0; for (int i = 0; i < n; i++) { auto& history = pos[a[i]]; int la = history.back(); if (history.size() > 1) { int lla = history[history.size() - 2]; sg.update(1, 1, n, lla + 1, la, -1); } int cur = i + 1; sg.update(1, 1, n, la + 1, cur, 1); auto [minn, cnt] = sg.query(1, 1, n, 1, cur); ans += cur - (minn == 0 ? cnt : 0); history.push_back(cur); } cout << ans << '\n'; return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/18091945
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步