Skip to content

Commit 6fdad0d

Browse files
committed
add abc382
1 parent 41062b4 commit 6fdad0d

File tree

3 files changed

+376
-1
lines changed

3 files changed

+376
-1
lines changed

docs/algorithm/AtCoder/abc382.md

+373
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
## [A - Daily Cookie](https://atcoder.jp/contests/abc382/tasks/abc382_a)
2+
3+
???+ Abstract "题目大意"
4+
5+
有 $N$ 个盒子,给你一个长度为 $N$ 的字符串 $S$,如果 $S_i$ 是 `@`,表示第 $i$ 个盒子有一块饼干,如果 $S_i$ 是 `.`,表示第 $i$ 个盒子是空的。如果吃掉 $D$ 块饼干(保证 $N$ 个盒子中至少有 $D$ 块饼干),还剩多少块饼干?
6+
7+
??? Success "参考代码"
8+
9+
=== "C++"
10+
11+
```c++
12+
#include <algorithm>
13+
#include <iostream>
14+
#include <string>
15+
16+
using namespace std;
17+
18+
int main()
19+
{
20+
int n, d;
21+
string s;
22+
cin >> n >> d >> s;
23+
int cnt = count(s.begin(), s.end(), '.');
24+
cout << cnt + d << endl;
25+
return 0;
26+
}
27+
```
28+
29+
---
30+
31+
## [B - Daily Cookie 2](https://atcoder.jp/contests/abc382/tasks/abc382_b)
32+
33+
???+ Abstract "题目大意"
34+
35+
有 $N$ 个盒子,给你一个长度为 $N$ 的字符串 $S$,如果 $S_i$ 是 `@`,表示第 $i$ 个盒子有一块饼干,如果 $S_i$ 是 `.`,表示第 $i$ 个盒子是空的。有人打算吃掉 $D$ 块饼干(保证 $N$ 个盒子中至少有 $D$ 块饼干),他每一次会选择最靠右且有饼干的盒子,吃掉其中的饼干。在他吃掉 $D$ 块饼干后, 从左到右输出 $N$ 个盒子是否有饼干。
36+
37+
??? Success "参考代码"
38+
39+
=== "C++"
40+
41+
```c++
42+
#include <iostream>
43+
#include <string>
44+
45+
using namespace std;
46+
47+
int main()
48+
{
49+
int n, d;
50+
string s;
51+
cin >> n >> d >> s;
52+
for(int i = n - 1; i >= 0 && d; i--)
53+
if(s[i] == '@')
54+
{
55+
d--;
56+
s[i] = '.';
57+
}
58+
cout << s << endl;
59+
return 0;
60+
}
61+
```
62+
63+
---
64+
65+
## [C - Kaiten Sushi](https://atcoder.jp/contests/abc382/tasks/abc382_c)
66+
67+
???+ Abstract "题目大意"
68+
69+
有 $N(1 \le N \le 2 \times 10 ^ 5)$ 去吃寿司,不同的寿司有不同的美味度,第 $i$ 个人对寿司美味度的要求是至少为 $A_i$,即,如果第 $i$ 个人看见了美味度大于等于 $A_i$ 的寿司,就会吃掉。
70+
71+
这 $N$ 个人从左到右(第 $1$ 个人在最左,第 $N$ 个人在最右)坐成一行,寿司在传送带上,传送带的方向是从左到右的,因此寿司总是先被第 $1$ 个人看见。一共有 $M(1 \le M \le 2 \times 10 ^ 5)$ 个寿司,第 $i$ 个寿司的美味度是 $B_i$。如果第 $i$ 个人看见了美味度大于等于 $A_i$ 的寿司,就会吃掉这个寿司,被吃掉的寿司不会被后面的人看见。如果第 $i$ 个人看见了美味度小于 $A_i$ 的寿司,会无视掉这个寿司,随后这个寿司将被下一个人看见。每个人可以吃下无数个寿司。
72+
73+
问:这个 $M$ 个寿司分别是被谁吃下的?按顺序输出第 $i$ 个寿司是被哪个人吃下的。(如果某个寿司没有任何人吃,输出 `-1`)
74+
75+
??? Note "解题思路"
76+
77+
第 $i$ 个人的美味度要求是 $A_i$,则所有美味度大于等于 $A_i$ 的寿司都会被他吃掉,因此后面大于等于 $A_i$ 的人都会被屏蔽掉。所以只需要从 $A_i$ 开始,求出连续递减的子序列,则只有这个子序列的人会吃到寿司。
78+
79+
??? Success "参考代码"
80+
81+
=== "C++"
82+
83+
```c++
84+
#include <functional>
85+
#include <iostream>
86+
#include <limits>
87+
#include <map>
88+
89+
using namespace std;
90+
91+
int main()
92+
{
93+
int n, m;
94+
cin >> n >> m;
95+
map<int, int, greater<int>> pos;
96+
for (int i = 1, last = numeric_limits<int>::max(); i <= n; i++)
97+
{
98+
int a;
99+
cin >> a;
100+
if(a < last)
101+
{
102+
pos[a] = i;
103+
last = a;
104+
}
105+
}
106+
while (m--)
107+
{
108+
int b;
109+
cin >> b;
110+
auto it = pos.lower_bound(b);
111+
cout << (it == pos.end() ? -1 : it->second) << endl;
112+
}
113+
return 0;
114+
}
115+
```
116+
117+
---
118+
119+
## [D - Keep Distance](https://atcoder.jp/contests/abc382/tasks/abc382_d)
120+
121+
???+ Abstract "题目大意"
122+
给你两个整数 $N(2 \le N \le 12)$ 和 $M(10N-9 \le M \le 10N)$
123+
124+
按照字典序输出所有满足以下条件的序列 $(A_1, A_2, ..., A_N)$:
125+
126+
- $1 \le A_i$
127+
- 对于所有的 $i \in [2, N]$,有 $A_{i-1} + 10 \le A_i$
128+
- $A_N \le M$
129+
130+
??? Note "解题思路"
131+
132+
dfs 模板题。
133+
134+
??? Success "参考代码"
135+
136+
=== "C++"
137+
138+
```c++
139+
#include <iostream>
140+
#include <vector>
141+
142+
using namespace std;
143+
144+
int main()
145+
{
146+
int n, m;
147+
cin >> n >> m;
148+
vector<vector<int>> ans;
149+
vector<int> now(n);
150+
151+
auto dfs = [&ans, &now, n, m](auto &self, int cur, int last) -> void
152+
{
153+
if (cur == n)
154+
{
155+
ans.push_back(now);
156+
return;
157+
}
158+
159+
int cnt = n - cur - 1;
160+
for (int i = last; i + 10 * cnt <= m; i++)
161+
{
162+
now[cur] = i;
163+
self(self, cur + 1, 10 + i);
164+
}
165+
};
166+
167+
dfs(dfs, 0, 1);
168+
cout << ans.size() << endl;
169+
for(auto &v : ans)
170+
{
171+
for(auto x : v)
172+
cout << x << ' ';
173+
cout << endl;
174+
}
175+
return 0;
176+
}
177+
```
178+
179+
---
180+
181+
## [E - Expansion Packs](https://atcoder.jp/contests/abc382/tasks/abc382_e)
182+
183+
???+ Abstract "题目大意"
184+
185+
有无限多个卡包,每个卡包有 $N(1 \le N \le 5000)$ 张卡。在每个卡包中,第 $i$ 张卡有 $P_i\%$ 的概率是稀有卡,其中 $1 \le P_i \le 100$。每张卡是否稀有相互独立。
186+
187+
你将一直打开新卡包,并获得每个打开的卡包中的所有卡,直到累计获得至少 $X(1 \le X \le 5000)$ 张稀有卡为止。
188+
189+
问:当你停下时,打开的卡包数量的数学期望是多少?
190+
191+
??? Note "解题思路"
192+
193+
因为每个卡包内的每一张卡的稀有概率是固定的,可以处理出一个 $g[i][j]$ 表示在一个卡包的前 $i$ 张卡中开出 $j$ 张稀有卡的概率,显然有 $g[i][j] = g[i][j] \times \frac{100-P_i}{100} + g[i-1][j-1] \times \frac{P_i}{100}$,可以在 $O(N^2)$ 的时间预处理出来,则 $g[N][j]$ 就表示开一个卡包获得 $j$ 张稀有卡的概率。
194+
195+
设 $dp[i]$ 表示累计获得至少 $i$ 张稀有卡需要打开卡包数量的数学期望,初始的时候有 $dp[0] = 0$,转移时,枚举新打开一个卡包能获得多少张稀有卡,一个卡包有 $N$ 张卡,最多可获得 $N$ 张稀有卡,最少获得 $0$ 张,因此枚举 $0 \le j \le N$,即:$dp[i] = 1 + \sum\limits_{j=0}^Ndp[i-j]g[N][j]$
196+
197+
然后我们会发现两个问题,首先 $i-j$ 是有可能小于 $0$ 的,这可以通过 $\max(i-j, 0)$ 避免,也就时 $dp[i] = 1 + \sum\limits_{j=0}^Ndp[\max(i-j, 0)]g[N][j]$,另一个问题是,当 $j = 0$ 时,右式会出现 $dp[i]$,但是这个式子就是为了求出 $dp[i]$ 的,所以我们变换一下:
198+
199+
$$
200+
\begin{align*}
201+
dp[i] & = 1 + \sum\limits_{j=0}^Ndp[\max(i-j, 0)]g[N][j] \\
202+
dp[i] & = 1 + \sum\limits_{j=1}^Ndp[\max(i-j, 0)]g[N][j] + dp[i]g[N][0] \\
203+
(1-g[N][0])dp[i] & = 1 + \sum\limits_{j=1}^Ndp[\max(i-j, 0)]g[N][j] \\
204+
dp[i] & = \frac{1 + \sum\limits_{j=1}^Ndp[\max(i-j, 0)]g[N][j]}{1-g[N][0]} \\
205+
\end{align*}
206+
$$
207+
208+
又因为 $1 \le p \le 100$,所以 $g[N][0]$ 一定不等于 $1$,因此这个变换是合法的。这样就能算了,预处理 $g$ 的时间复杂度是 $O(N^2)$,计算 $dp$ 的时间复杂度是 $O(NX)$,总复杂度就是 $O(N^2+NX)$
209+
210+
??? Success "参考代码"
211+
212+
=== "C++"
213+
214+
```c++
215+
#include <iomanip>
216+
#include <iostream>
217+
#include <vector>
218+
219+
using namespace std;
220+
221+
int main()
222+
{
223+
int n, x;
224+
cin >> n >> x;
225+
vector<double> g(n+1);
226+
g[0] = 1;
227+
for(int i = 1; i <= n; i++)
228+
{
229+
double p;
230+
cin >> p;
231+
for(int j = i; j; j--)
232+
g[j] = g[j] * (100-p)/100 + g[j-1] * p / 100;
233+
g[0] *= (100-p)/100;
234+
}
235+
vector<double> dp(x+1);
236+
for(int i = 1; i <= x; i++)
237+
{
238+
double sum = 1;
239+
for(int j = 1; j <= n; j++)
240+
sum += dp[max(i-j, 0)] * g[j];
241+
dp[i] = sum / (1 - g[0]);
242+
}
243+
cout << setprecision(8) << dp[x] << endl;
244+
return 0;
245+
}
246+
```
247+
248+
---
249+
250+
## [F - Falling Bars](https://atcoder.jp/contests/abc382/tasks/abc382_f)
251+
252+
???+ Abstract "题目大意"
253+
254+
有一个 $H \times W(1 \le H, W \le 2 \times 10 ^ 5)$ 的网格。网格中有 $N(1 \le N \le 2 \times 10 ^ 5)$ 个高度为 $1$ 格的方块,每个方块的初始位置用三个数字 $(R_i, C_i, L_i)$ 来描述,表示这个方块占据了 $(R_i, C_i), (R_i, C_i+1), ..., (R_i, C_i+L-1)$ 的位置。输入数据保证这 $N$ 个方块是不重叠的。
255+
256+
初始的时间是 $t=0$,在每个 $t=0.5+n$(其中 $n$ 表示自然数)的时刻,会发生以下事情:
257+
258+
- 首先,按顺序遍历所有的方块,对于所有的 $i = 1, 2, ..., N$,假设第 $i$ 个方块当前的位置是 $(R_i, C_i), (R_i, C_i+1), ..., (R_i, C_i+L-1)$,如果 $R_i + 1 \le W$,且 $(R_i+1, C_i), (R_i + 1, C_i+1), ..., (R_i + 1, C_i+L-1)$ 都没有被占据,则第 $i$ 个方块整体向下移动 $1$ 格,占据这些位置。即:如果第 $i$ 个方块没有到达网格的底部,且方块下方 $1$ 格没有被占据,就整体下移 $1$ 格。
259+
- 否则,如果第 $i$ 个方块处于网格的底部,或者其下方 $1$ 格的位置被占据,则跳过第 $i$ 个方块的操作。
260+
261+
![img](https://img.atcoder.jp/abc382/57581b182e43915bce2b78747acfa2a6.png)
262+
263+
问:在 $t = 10^{100}$,这 $N$ 个方块的 $R_i$ 分别是多少?例子见上图。
264+
265+
??? Note "解题思路"
266+
267+
可以发现,像上图的这样的情况,无论这个网格有多大,第 $2$ 个方块都会保持在最底端,所以为了加快计算,不能一格一格的移动,应该一次性移动多格。为了移动多格,我们需要算出某一段最高的可被覆盖的位置,这可以用线段树维护一个区间最小值实现。
268+
269+
具体来说,我们用 $h[i]$ 表示第 $i$ 列最上方未被占据的行号,初始值就是 $h[i] = W$,当某个方块下落时,假设当前方块占据的是 $(R_i, C_i), (R_i, C_i+1), ..., (R_i, C_i+L-1)$,就查询 $h[C_i]$ 到 $h[C_i + L_i - 1]$ 这一个区间的最小值,模拟下落被卡住的情况。确定下落的位置后,方块占据这一段,因此这一段的值也要修改,涉及到区间修改和区间最小值查询,所以用线段树维护这个 $h$ 数组即可。
270+
271+
实际操作的时候,还可以注意到,一定是越靠近底部的方块先确定最终位置,所以按照 $R_i$ 降序排序即可,这样每一次操作都可以确定一个方块的最终位置,一共 $N$ 个方块,单次线段树的修改和查询操作时间复杂度是 $O(\log W)$,因此总的时间复杂度就是 $O(N\log W)$
272+
273+
??? Success "参考代码"
274+
275+
=== "C++"
276+
277+
```c++
278+
#include <algorithm>
279+
#include <iostream>
280+
#include <limits>
281+
#include <vector>
282+
283+
using namespace std;
284+
285+
struct SegTree
286+
{
287+
struct Node
288+
{
289+
int m;
290+
bool lazy = false;
291+
Node(int x) : m(x) {}
292+
};
293+
294+
vector<Node> t;
295+
296+
SegTree(int n, int val) : t(4 * n, val) {}
297+
298+
void push_down(int p)
299+
{
300+
if (t[p].lazy)
301+
{
302+
int lch = p * 2, rch = p * 2 + 1;
303+
t[lch].m = t[rch].m = t[p].m;
304+
t[lch].lazy = t[rch].lazy = true;
305+
t[p].lazy = false;
306+
}
307+
}
308+
309+
int query(int p, int beg, int end, int l, int r)
310+
{
311+
if (end < l || beg > r)
312+
return numeric_limits<int>::max();
313+
if (beg >= l && end <= r)
314+
return t[p].m;
315+
push_down(p);
316+
int mid = (beg + end) / 2;
317+
int lch = p * 2, rch = p * 2 + 1;
318+
return min(query(lch, beg, mid, l, r), query(rch, mid + 1, end, l, r));
319+
}
320+
321+
void update(int p, int beg, int end, int l, int r, int x)
322+
{
323+
if (end < l || beg > r)
324+
return;
325+
if (beg >= l && end <= r)
326+
{
327+
t[p].m = x;
328+
t[p].lazy = true;
329+
return;
330+
}
331+
push_down(p);
332+
int mid = (beg + end) / 2;
333+
int lch = p * 2, rch = p * 2 + 1;
334+
update(lch, beg, mid, l, r, x);
335+
update(rch, mid + 1, end, l, r, x);
336+
t[p].m = min(t[p].m, x);
337+
}
338+
};
339+
340+
struct Bar
341+
{
342+
int depth, left, right, id;
343+
};
344+
345+
int main()
346+
{
347+
int h, w, n;
348+
cin >> h >> w >> n;
349+
SegTree t(w, h);
350+
vector<Bar> bars(n);
351+
for (int i = 0; i < n; i++)
352+
{
353+
int r, c, l;
354+
cin >> r >> c >> l;
355+
bars[i] = {r, c, c + l - 1, i};
356+
}
357+
sort(bars.begin(), bars.end(), [](const Bar &x, const Bar &y) -> bool
358+
{ return x.depth > y.depth; });
359+
vector<int> ans(n);
360+
for (auto b : bars)
361+
{
362+
int m = t.query(1, 1, w, b.left, b.right);
363+
ans[b.id] = m;
364+
t.update(1, 1, w, b.left, b.right, m - 1);
365+
}
366+
for (auto x : ans)
367+
cout << x << endl;
368+
return 0;
369+
}
370+
```
371+
372+
---
373+

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
最近更新:
1212

13+
- (20241231) [ABC382(A-F) 题解](./algorithm/AtCoder/abc382.md)
1314
- (20241230) [C++ 算法模板 for VSCode](./algorithm/cpp_templete.md)
1415
- (20241230) [ABC381(A-F) 题解](./algorithm/AtCoder/abc381.md)
1516
- (20241228) [ABC380(A-G) 题解](./algorithm/AtCoder/abc380.md)

mkdocs.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ nav:
99
- 算法:
1010
- algorithm/index.md
1111
- AtCoder:
12+
- ABC382(A-F): algorithm/AtCoder/abc382.md
1213
- ABC381(A-F): algorithm/AtCoder/abc381.md
1314
- ABC380(A-G): algorithm/AtCoder/abc380.md
1415
- ABC379(A-G): algorithm/AtCoder/abc379.md
@@ -50,8 +51,8 @@ nav:
5051
- ABC310(A-F): algorithm/AtCoder/abc310.md
5152
- ABC309(A-F): algorithm/AtCoder/abc309.md
5253
- ABC308(A-G): algorithm/AtCoder/abc308.md
53-
- 笔记: algorithm/note.md
5454
- C++ 算法模板 for VSCode: algorithm/cpp_templete.md
55+
- 笔记: algorithm/note.md
5556

5657
- 开发:
5758
- dev/index.md

0 commit comments

Comments
 (0)