AtCoder Beginner Contest 382
省流版
- A. 统计计数即可
- B. 模拟更改即可
- C. 考虑每个寿司,找到满足条件的位置最小值,即一个前缀最小值
- D. 搜索剪枝即可
- E. 期望题,根据期望定义写出转移式,从生成函数的角度求翻出 张稀有牌的概率
- F. 从深度最大的横条开始,考虑下落时求解的每列深度然后更新,用线段树维护即可
A - Daily Cookie (abc382 A)
题目大意
给定一个包含@.
的长度为的字符串,给定d
,表示将d
个@
变成.
,问.
的数量。
解题思路
统计@
的数量,然后减去d
,再用减去这个值即可。
神奇的代码
n, d = map(int, input().split()) s = input().strip() print(n - sum(1 for i in s if i == '@') + d)
B - Daily Cookie 2 (abc382 B)
题目大意
给定一个包含@.
的长度为的字符串,给定d
,表示将最右边d
个@
变成.
,最终字符串。
解题思路
找到最右边的d
个@
的下标,然后将其变成.
即可。
神奇的代码
n, d = map(int, input().split()) s = list(input().strip()) pos = [i for i in range(n) if s[i] == '@'] pos = pos[-d:] for i in pos: s[i] = '.' s = ''.join(s) print(s)
C - Kaiten Sushi (abc382 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> a(n); for (auto& x : a) cin >> x; vector<int> id(n); iota(id.begin(), id.end(), 0); sort(id.begin(), id.end(), [&](int i, int j) { return a[i] < a[j]; }); vector<int> minn(n); minn[0] = id[0]; for (int i = 1; i < n; ++i) { minn[i] = min(minn[i - 1], id[i]); } for (int i = 0; i < m; ++i) { int b; cin >> b; auto pos = upper_bound(id.begin(), id.end(), b, [&](int x, int y) { return a[y] > x; }) - id.begin(); if (pos == 0) { cout << -1 << '\n'; } else { cout << minn[pos - 1] + 1 << '\n'; } } return 0; }
D - Keep Distance (abc382 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, m; cin >> n >> m; vector<vector<int>> ans; vector<int> tmp(n); auto dfs = [&](auto&& dfs, int pos) -> void { if (pos == n && tmp[pos - 1] <= m) { ans.push_back(tmp); return; } int st = pos ? tmp[pos - 1] + 10 : 1; for (int i = st; i <= m; ++i) { if (i + 10 * (n - pos - 1) > m) continue; tmp[pos] = i; dfs(dfs, pos + 1); } }; dfs(dfs, 0); cout << ans.size() << '\n'; for (auto& i : ans) { for (auto& j : i) cout << j << ' '; cout << '\n'; } return 0; }
E - Expansion Packs (abc382 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, x; cin >> n >> x; vector<int> po(n); for (auto& i : po) cin >> i; vector<double> dp(x + 1, 0); vector<double> p(n + 1, 0); p[0] = 1; for (int i = 0; i < n; ++i) { vector<double> p1(n + 1, 0), p2(n + 1, 0); for (int j = 0; j <= n; ++j) { p1[j] = p[j] * (100 - po[i]) / 100.0; if (j > 0) p2[j] = p[j - 1] * po[i] / 100.0; } for (int j = 0; j <= n; ++j) { p[j] = p1[j] + p2[j]; } } dp[x] = 0; for (int i = x - 1; i >= 0; --i) { for (int j = 1; j <= n; ++j) { dp[i] += ((i + j > x ? 0 : dp[i + j])) * p[j]; } dp[i] = (1 + dp[i]) / (1 - p[0]); } cout << fixed << setprecision(10) << dp[0] << '\n'; return 0; }
F - Falling Bars (abc382 F)
题目大意
给定一个 的网格,网格中有 个横条,横条长度为,位于 。初始时横条没有重叠。
然后所有横条都会往下落( 增大),如果下面没有横条的话,横条都会往下移动一个单位。否则不会移动。
问最后每个位置的横条的所处的行。
解题思路
首先考虑 最大的横条,它会落到最下面,因此列 的深度变为了 ,依次类推,考虑第 个横条,其列为 ,那它最后落到的行数是多少呢?那就是 的深度最小值,落完后,更新一下 的深度即可。
即维护数组 表示第 列无横条的最深深度,然后按照 从大到小的顺序处理,每次查询 深度的最小值,那该横条就落在深度为上,然后更新 的深度为 。区间查询和区间修改,用线段树维护该数组即可。时间复杂度为 。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int N = 2e5 + 8; const int inf = 1e9 + 7; class segment { #define lson (root << 1) #define rson (root << 1 | 1) public: int minn[N << 2]; int lazy[N << 2]; void build(int root, int l, int r, int deep) { if (l == r) { minn[root] = deep; lazy[root] = inf; return; } int mid = (l + r) >> 1; build(lson, l, mid, deep); build(rson, mid + 1, r, deep); minn[root] = min(minn[lson], minn[rson]); lazy[root] = inf; } void pushup(int root) {} void pushdown(int root) { if (lazy[root] != inf) { minn[lson] = min(minn[lson], lazy[root]); minn[rson] = min(minn[rson], lazy[root]); lazy[lson] = min(lazy[lson], lazy[root]); lazy[rson] = min(lazy[rson], lazy[root]); lazy[root] = inf; } } void update(int root, int l, int r, int L, int R, int val) { if (L <= l && r <= R) { minn[root] = min(minn[root], val); lazy[root] = min(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]); } int query(int root, int l, int r, int L, int R) { if (L <= l && r <= R) { return minn[root]; } pushdown(root); int mid = (l + r) >> 1; int resl = inf, resr = inf; if (L <= mid) resl = query(lson, l, mid, L, R); if (R > mid) resr = query(rson, mid + 1, r, L, R); return min(resl, resr); } } sg; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int h, w, n; cin >> h >> w >> n; vector<array<int, 3>> seg(n); for (auto& [r, c, l] : seg) cin >> r >> c >> l; vector<int> id(n); iota(id.begin(), id.end(), 0); sort(id.begin(), id.end(), [&](int i, int j) { return seg[i][0] > seg[j][0]; }); vector<int> ans(n); sg.build(1, 1, w, h); for (auto& i : id) { auto [r, c, l] = seg[i]; ans[i] = sg.query(1, 1, w, c, c + l - 1); sg.update(1, 1, w, c, c + l - 1, ans[i] - 1); } for (auto x : ans) cout << x << '\n'; return 0; }
G - Tile Distance 3 (abc382 G)
题目大意
<++>
解题思路
<++>
神奇的代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架