AtCoder Beginner Contest 379
省流版
- A. 模拟即可
- B. 贪心,有个就吃,模拟即可
- C. 维护已经有棋子的格子,有多个棋子往右推,代价等差数列求和,模拟即可
- D. 注意到植物高度=
当前天
-种植天
,维护植物的种植天然后二分找对应高度的植物即可 - E. 考虑最终答案每一个数位的值,然后处理进位即可
- F. 单调栈处理建筑能看的建筑数量,然后求 最大高度,二分找到屏蔽单调栈的楼数量即可
- G. 维护轮廓线状态,仅从有效状态往后即可
A - Cyclic (abc379 A)
题目大意
给定三个数字,将其进行两次循环移位并输出。
解题思路
可以用rotate
函数,将首字母移动到末尾。
神奇的代码
#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 a; cin >> a; rotate(a.begin(), a.begin() + 1, a.end()); cout << a << ' '; rotate(a.begin(), a.begin() + 1, a.end()); cout << a << '\n'; return 0; }
B - Strawberries (abc379 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); int n, k; string s; cin >> n >> k >> s; int ans = 0; string good = string(k, 'O'); for (int i = 0; i < n; i++) { if (s.substr(i, k) == good) { ++ans; fill(s.begin() + i, s.begin() + i + k, 'X'); } } cout << ans << '\n'; return 0; }
C - Sowing Stones (abc379 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, m; cin >> n >> m; vector<int> x(m), a(m); for (auto& i : x) cin >> i; for (auto& i : a) cin >> i; if (accumulate(a.begin(), a.end(), 0ll) != n) { cout << -1 << '\n'; } else { vector<int> id(m); iota(id.begin(), id.end(), 0); sort(id.begin(), id.end(), [&](int i, int j) { return x[i] < x[j]; }); LL ans = 0; int cur = 1; auto calc = [&](int l, int r, int x) { int cnt = r - l + 1; return (0ll + l + r) * cnt / 2 - 1ll * x * cnt; }; bool ok = true; for (auto& i : id) { if (cur < x[i]) { ok = false; break; } ans += calc(cur, cur + a[i] - 1, x[i]); cur += a[i]; } if (!ok) { ans = -1; } cout << ans << '\n'; } return 0; }
D - Home Garden (abc379 D)
题目大意
问题陈述
高桥有 个花盆。最初,他没有种植任何植物。
依次处理 个操作,分三种
1
:准备一个空花盆并放入一株植物。此时植物的高度是 。2 T
:等待 天,现有植物的高度会增加 。3 H
:收获所有高度至少达到 的植株,并输出收获植株的数量。收获的植物会从花盆中移出。
解题思路
植物高度为当前天
-种植天
,因此我们只需维护每个植物的种植天
,对于操作三,假设当天是第天 ,那只需把所有种植天
的植物全收割即可。
因为植物的 种植天
是不断递增的,因此就用一个数组维护一个递增的种植天,然后再用一个变量维护,已经被收割植物 的种植天
的最大值,每次收割,二分找到的位置,其与 的差值就是本次收割的植株数量。
神奇的代码
#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; vector<LL> pot; int l = 0; LL day = 0; while (q--) { int op; cin >> op; if (op == 1) { pot.push_back(day); } else if (op == 2) { int t; cin >> t; day += t; } else if (op == 3) { int h; cin >> h; int nxt = upper_bound(pot.begin() + l, pot.end(), day - h) - pot.begin(); cout << nxt - l << '\n'; l = nxt; } } return 0; }
E - Sum of All Substrings (abc379 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; string s; cin >> n >> s; LL sum = 0; for (int i = 0; i < n; ++i) sum += (i + 1) * (s[i] - '0'); vector<LL> ans; ans.push_back(0); for (int i = n - 1; i >= 0; --i) { ans.back() += sum % 10; int jin = ans.back() / 10; ans.back() %= 10; ans.push_back(sum / 10 + jin); sum -= (i + 1) * (s[i] - '0'); } while (ans.back() >= 10) { int jin = ans.back() / 10; ans.back() %= 10; ans.push_back(jin); } while (ans.back() == 0) ans.pop_back(); reverse(ans.begin(), ans.end()); for (auto& i : ans) cout << i; cout << '\n'; return 0; }
F - Buildings 2 (abc379 F)
题目大意
给定个建筑的高度,回答 个询问。
每个询问给定 ,问从 建筑往右看,都能看到的建筑的数量。
如果建筑能看到建筑 ,则不存在 ,满足 。
解题思路
如果单问某个建筑往右看,能看到的建筑物数量,其实就是一个从右往左的单调栈——高的建筑会屏蔽矮的建筑,从而矮的建筑会出栈,使得栈是一个从栈顶到栈低是一个递增的情况。
询问可以离线,因此我们按照从大到小的顺序考虑每个询问,当前单调栈的结果是从往右看能看到的建筑物高度,然后考虑它们的有哪些看不到。
注意到其建筑看到的条件:
- 如果建筑能看到建筑 ,则不存在 ,满足 。
不变, 序号越小,其看到的条件就越苛刻,即 能看到的建筑, 一定能看到。反之不能看到的话,却不一定。
但我们要找 都能看到的建筑,其实就是 能能看到的建筑数量,按照单调栈的做法,我们即找到[l+1,r]中最高的建筑,它会屏蔽单调栈里高度小于 的建筑。
无修改的区间找最大值可以用 表,然后找出屏蔽建筑物的数量,因为单调栈是单调的,因此可以二分找到屏蔽建筑的分界线,进而得出可以看到的建筑物的数量。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; template <typename T, class F = function<T(const T&, const T&)>> class SparseTable { public: int n; vector<vector<T>> mat; F func; SparseTable(const vector<T>& a, const F& f) : func(f) { n = static_cast<int>(a.size()); int max_log = 32 - __builtin_clz(n); mat.resize(max_log); mat[0] = a; for (int j = 1; j < max_log; j++) { mat[j].resize(n - (1 << j) + 1); for (int i = 0; i <= n - (1 << j); i++) { mat[j][i] = func(mat[j - 1][i], mat[j - 1][i + (1 << (j - 1))]); } } } T get(int from, int to) const { // [from, to] assert(0 <= from && from <= to && to <= n - 1); int lg = 32 - __builtin_clz(to - from + 1) - 1; return func(mat[lg][from], mat[lg][to - (1 << lg) + 1]); } }; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, q; cin >> n >> q; vector<int> h(n); for (auto& i : h) cin >> i; SparseTable<int> st(h, [&](int i, int j) { return max(i, j); }); vector<array<int, 2>> query(q); for (auto& i : query) cin >> i[0] >> i[1]; vector<int> id(q); iota(id.begin(), id.end(), 0); sort(id.begin(), id.end(), [&](int a, int b) { return query[a][1] > query[b][1]; }); vector<int> ans(q); vector<int> stack; int cur = n - 1; for (auto i : id) { auto [l, r] = query[i]; --l, --r; while (cur > r) { int hei = h[cur]; while (!stack.empty() && hei >= stack.back()) { stack.pop_back(); } stack.push_back(hei); --cur; } int hei = st.get(l + 1, r); auto pos = upper_bound(stack.begin(), stack.end(), hei, greater<int>()) - stack.begin(); ans[i] = pos; } for (auto i : ans) cout << i << '\n'; return 0; }
G - Count Grid 3-coloring (abc379 G)
题目大意
给定的格子,每个格子写着 ?123
中的一种。
现将所有的?
替换成123
中的一个。
问有多少替换方法,使得相邻格子的数字不相同。
解题思路
从上到下,从左到右依次考虑每行每列的格子,看其?
能否替换成123
,取决于上一行的数字和上一列的数字是否与该格子相同。
因此我们的状态得保留这两处格子的信息,但是仅仅保留这两处的话,当前状态变成下一列时,无法继承之前的状态。
怎样才能继承之前的状态呢?那就需要保留一个轮廓的状态(即已考虑的格子的边界)。如下图蓝色格子所示。此即为轮廓线。
即 表示从上到下,从左到右考虑到格子 ,外围一圈的数字状态为 (这里是一个 的压缩状态)。
转移即考虑当前格子的数字是什么,是否合法即可。由于 ,因此 ,保持 ,则时间复杂度为 ,有 。
但考虑到的有效 状态数没有(相邻相同的数字是非法状态),而是 (因为不能和前一列相同,因此该列的数字只有两种,然后还有一些组合数的系数),因此仅从有效状态转移,时间复杂度为 。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int mo = 998244353; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int h, w; cin >> h >> w; vector<string> s(h); for (auto& i : s) cin >> i; if (h < w) { vector<string> t(w, string(h, ' ')); for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { t[j][i] = s[i][j]; } } swap(h, w); swap(s, t); } vector<int> base(w, 1); for (int i = 1; i < w; ++i) { base[i] = base[i - 1] * 3; } auto get_bit = [&](int x, int y) { return x / base[y] % 3; }; auto tr = [&](int cur, int pos, int v) { int ret = cur; ret -= get_bit(cur, pos) * base[pos]; ret += v * base[pos]; return ret; }; auto ok = [&](int x, int y, int cur, int v) { if (y != 0 && get_bit(cur, y - 1) == v) return false; if (x != 0 && get_bit(cur, y) == v) return false; return true; }; int up = 1; for (int i = 0; i < w; i++) { up *= 3; } map<int, int> dp; dp[0] = 1; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { map<int, int> ndp; for (auto& [k, v] : dp) { if (s[i][j] != '?') { if (ok(i, j, k, s[i][j] - '1')) { auto nxt = tr(k, j, s[i][j] - '1'); ndp[nxt] += v; if (ndp[nxt] >= mo) ndp[nxt] -= mo; } } else { for (int l = 0; l <= 2; ++l) { if (ok(i, j, k, l)) { auto nxt = tr(k, j, l); ndp[nxt] += v; if (ndp[nxt] >= mo) ndp[nxt] -= mo; } } } } dp.swap(ndp); } } int ans = 0; for (auto& [k, v] : dp) { ans += v; if (ans >= mo) ans -= mo; } cout << ans << '\n'; return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/18548361
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2022-11-15 Codeforces Round #833 (Div. 2)