Skip to content

Commit ffa93c3

Browse files
committed
add abc391
1 parent fb2774a commit ffa93c3

File tree

5 files changed

+370
-1
lines changed

5 files changed

+370
-1
lines changed

docs/algorithm/AtCoder/abc391.md

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
## [A - Lucky Direction](https://atcoder.jp/contests/abc391/tasks/abc391_a)
2+
3+
???+ Abstract "题目大意"
4+
5+
给你一个长度为 $1$ 或 $2$ 的方向字符串 $S$,$S$ 可能的取值是:
6+
7+
- North: `N`
8+
- East: `E`
9+
- West: `W`
10+
- South: `S`
11+
- Northeast: `NE`
12+
- Northwest: `NW`
13+
- Southeast: `SE`
14+
- Southwest: `SW`
15+
16+
输出 $S$ 的相反方向。
17+
18+
??? Success "参考代码"
19+
20+
=== "C++"
21+
22+
```c++
23+
#include <iostream>
24+
#include <unordered_map>
25+
26+
using namespace std;
27+
28+
int main()
29+
{
30+
unordered_map<char, char> mp = {{'S', 'N'}, {'N', 'S'}, {'W', 'E'}, {'E', 'W'}};
31+
string d;
32+
cin >> d;
33+
for(auto c : d)
34+
cout << mp[c];
35+
return 0;
36+
}
37+
```
38+
39+
---
40+
41+
## [B - Seek Grid](https://atcoder.jp/contests/abc391/tasks/abc391_b)
42+
43+
???+ Abstract "题目大意"
44+
45+
给你一个 $N \times N(1 \le N \le 50)$ 二维矩阵 $S$ 和一个 $M \times M(1 \le M \le N)$ 的二维矩阵 $T$。已知在 $S$ 存在一个 $M \times M$ 的子矩阵等于 $T$,输出这个子矩阵的左上角的坐标。
46+
47+
??? Success "参考代码"
48+
49+
=== "C++"
50+
51+
```c++
52+
#include <iostream>
53+
#include <vector>
54+
55+
using namespace std;
56+
57+
int main()
58+
{
59+
int n, m;
60+
cin >> n >> m;
61+
vector<string> s(n), t(m);
62+
for (auto &ss : s)
63+
cin >> ss;
64+
for (auto &ss : t)
65+
cin >> ss;
66+
67+
auto check = [&s, &t, m](int a, int b) -> bool
68+
{
69+
for(int i = 0; i < m; i++)
70+
for(int j = 0; j < m; j++)
71+
if(s[a+i][b+j] != t[i][j])
72+
return false;
73+
return true;
74+
};
75+
76+
for(int a = 0; a + m <= n; a++)
77+
for(int b = 0; b + m <= n; b++)
78+
if(check(a, b))
79+
{
80+
cout << a + 1 << ' ' << b + 1 << endl;
81+
return 0;
82+
}
83+
return 0;
84+
}
85+
```
86+
87+
---
88+
89+
## [C - Pigeonhole Query](https://atcoder.jp/contests/abc391/tasks/abc391_c)
90+
91+
???+ Abstract "题目大意"
92+
93+
有 $N(2 \le N \le 10^6)$ 个鸽子和 $N$ 个鸟笼,初始的时候,第 $i$ 个鸽子在第 $i$ 个鸟笼中。
94+
95+
给你 $Q(1 \le Q \le 3 \times 10^5)$ 个询问,询问有两种类型:
96+
97+
- `1 Q H`:将第 $Q$ 个鸽子移动到第 $H$ 个鸟笼中。
98+
- `2`:回答有多少个笼子中的鸽子数量大于 $1$ 只。
99+
100+
??? Success "参考代码"
101+
102+
=== "C++"
103+
104+
```c++
105+
#include <iostream>
106+
#include <numeric>
107+
#include <vector>
108+
109+
using namespace std;
110+
111+
int main()
112+
{
113+
int n, q;
114+
cin >> n >> q;
115+
vector<int> nest(n+1), cnt(n+1, 1);
116+
iota(nest.begin(), nest.end(), 0);
117+
int ans = 0;
118+
while(q--)
119+
{
120+
int op;
121+
cin >> op;
122+
if(op == 1)
123+
{
124+
int p, h;
125+
cin >> p >> h;
126+
int from = nest[p];
127+
if(--cnt[from] == 1)
128+
ans--;
129+
if(++cnt[h] == 2)
130+
ans++;
131+
nest[p] = h;
132+
}
133+
else
134+
cout << ans << endl;
135+
}
136+
return 0;
137+
}
138+
```
139+
140+
---
141+
142+
## [D - Gravity](https://atcoder.jp/contests/abc391/tasks/abc391_d)
143+
144+
???+ Abstract "题目大意"
145+
146+
有一个 $10^9$ 行,$W$ 列的网格。网格中有 $N(1 \le W\le N \le 2 \times 10^5)$ 个 $1 \times 1$ 的方块。初始的时候,第 $i$ 个方块在 $(X_i, Y_i)$ 位置。
147+
148+
在时刻 $t = 1, 2, ..., 10^{100}$,每个方块都会按照以下规则移动:
149+
150+
1. 如果网格的最底行一整行都有方块,则消除最底行的所有方块。
151+
2. 对于剩余的方块,按照从下往上的顺序依次进行以下操作:
152+
- 若方块位于最底行,或其下方相邻单元格已有方块,则不进行任何操作。
153+
- 否则,将方块移动到下方相邻单元格。
154+
155+
给你 $Q(1 \le Q \le 2 \times 10^5)$ 个询问,每个询问给你两个整数 $T_i(1 \le T_i \le 10 ^ 9)$ 和 $A_i(1 \le A_i \le N)$,你需要回答在第 $T_i + 0.5$ 时刻第 $A_i$ 个方块是否还在网格中。
156+
157+
??? Note "解题思路"
158+
159+
首先把所有同一列的方块按照 $y$ 坐标排序,根据每一列有多少个方块,可以确定最终有多少行会被消除。每一行被消除的时间,是根据最靠上的方块的坐标决定的。所以只需要计算出每一行消除的时间,就能推算出这一行每个对应方块的消除时间。
160+
161+
??? Success "参考代码"
162+
163+
=== "C++"
164+
165+
```c++
166+
#include <algorithm>
167+
#include <iostream>
168+
#include <limits>
169+
#include <utility>
170+
#include <vector>
171+
172+
using namespace std;
173+
174+
int main()
175+
{
176+
int n, w;
177+
cin >> n >> w;
178+
vector<vector<pair<int, int>>> col(w + 1);
179+
for (int i = 1; i <= n; i++)
180+
{
181+
int x, y;
182+
cin >> x >> y;
183+
col[x].push_back({y, i});
184+
}
185+
int len = n;
186+
for (int i = 1; i <= w; i++)
187+
{
188+
len = min(len, (int)col[i].size());
189+
sort(col[i].begin(), col[i].end());
190+
}
191+
vector<int> remove_time(n + 1, numeric_limits<int>::max());
192+
for (int i = 0; i < len; i++)
193+
{
194+
int max_y = 0;
195+
for (int x = 1; x <= w; x++)
196+
{
197+
auto [y, _] = col[x][i];
198+
max_y = max(max_y, y);
199+
}
200+
for (int x = 1; x <= w; x++)
201+
{
202+
auto [_, id] = col[x][i];
203+
remove_time[id] = max_y;
204+
}
205+
}
206+
int q;
207+
cin >> q;
208+
while (q--)
209+
{
210+
int t, a;
211+
cin >> t >> a;
212+
cout << (t >= remove_time[a] ? "No" : "Yes") << '\n';
213+
}
214+
return 0;
215+
}
216+
```
217+
218+
---
219+
220+
## [E - Hierarchical Majority Vote](https://atcoder.jp/contests/abc391/tasks/abc391_e)
221+
222+
???+ Abstract "题目大意"
223+
224+
对于一个长度为 $3^n$ 的 $01$ 序列 $B = B_1\ B_2\cdots B_{3^n}$,定义以下操作来获得长度为 $3^{n-1}$ 的 $01$ 序列 $C = C_1\ C_2\cdots C_{3^{n-1}}$:
225+
226+
- 从 $B$ 序列的头部开始,每 $3$ 个为一组,保留 $3$ 个数字中的众数,插入到序列 $C$ 的末尾 。即对于 $i = 1,2, ...,3^{n-1}$,取出 $B_{3i-2}, B_{3i-1}, B_{3i}$ 中出现次数最多的值作为 $C_i$。
227+
228+
给你一个长度为 $3^N(1 \le N\le 13)$ 的 $01$ 序列 $A = A_1\ A_2\cdots A_{3^N}$,在对 $A$ 进行 $N$ 次操作之后,会得到一个长度为 $1$ 的序列 $A' = A'_1$。
229+
230+
现在,你可以将 $A$ 序列中的任意位置从 $0$ 改成 $1$ 或者从 $1$ 改成 $0$,问:最少要修改多少个位置,才能使 $A'_1$ 的值发生变化?
231+
232+
??? Note "解题思路"
233+
234+
首先对 $A$ 进行 $N$ 次操作得到结果 $A'_1$,同时保留每一次操作操作的中间结果。这一步操作的时间复杂度是 $3^1 + 3^2 + \cdots + 3^N = 3 \times\frac{1-3^N}{1-3} = \frac{3}{2}\times(3^N-1)$,这样的话,完全可以从结果开始倒推,暴力搜索可以得到结果。
235+
236+
??? Success "参考代码"
237+
238+
=== "C++"
239+
240+
```c++
241+
#include <algorithm>
242+
#include <iostream>
243+
#include <string>
244+
#include <vector>
245+
246+
using namespace std;
247+
248+
int main()
249+
{
250+
int n;
251+
cin >> n;
252+
vector<string> s(n + 1);
253+
cin >> s[0];
254+
for (int i = 1; i <= n; i++)
255+
{
256+
for (int j = 0; j < (int)s[i - 1].size(); j += 3)
257+
{
258+
int c = s[i - 1][j] + s[i - 1][j + 1] + s[i - 1][j + 2] - 3 * '0';
259+
s[i] += c > 1 ? '1' : '0';
260+
}
261+
}
262+
char oc = s[n][0];
263+
264+
auto dfs = [&s, oc](auto &self, int i, int j) -> int
265+
{
266+
if (s[i][j] != oc)
267+
return 0;
268+
if (i == 0)
269+
return 1;
270+
int cost[3] = {self(self, i - 1, 3 * j), self(self, i - 1, 3 * j + 1), self(self, i - 1, 3 * j + 2)};
271+
sort(cost, cost + 3);
272+
return cost[0] + cost[1];
273+
};
274+
275+
cout << dfs(dfs, n, 0) << endl;
276+
return 0;
277+
}
278+
```
279+
280+
---
281+
282+
## [F - K-th Largest Triplet](https://atcoder.jp/contests/abc391/tasks/abc391_f)
283+
284+
???+ Abstract "题目大意"
285+
286+
给你三个长度为 $N(1 \le 2 \times 10 ^5)$ 的正整数序列 $A = (A_1, A_2, ..., A_N)$,$B = (B_1, B_2, ..., B_N)$ 和 $C = (C_1, C_2, ..., C_N)$,其中 $1 \le A_i, B_i, C_i \le 10 ^ 9$。
287+
288+
对于所有满足 $1 \le i, j, k \le N$ 的整数三元组 $(i, j, k)$,一共有 $N ^ 3$ 个三元组,每个三元组 $(i, j, k)$ 都可以计算出 $A_iB_j + B_jC_k + C_kA_i$ 的值,求出第 $K(1 \le K \le \min(N^3, 5 \times 10 ^ 5))$ 大的值。
289+
290+
??? Note "解题思路"
291+
292+
首先将三个序列按降序排序。可以发现,如果固定 $i$ 和 $j$ 的值,则 $k$ 越小的对应值会越大,反之 $k$ 越大的对应值就会越小。
293+
294+
因此可以维护一个大顶堆,每次从大顶堆中取出 $(i, j, k)$,然后将 $(i+1, j, k)$,$(i, j+1, k)$ 和 $(i, j, k + 1)$ 加入堆中。这样一定能找出第 $K$ 大的值。至多会在堆中加入 $3K$ 个元素,时间复杂度为 $O(K\log K)$
295+
296+
??? Success "参考代码"
297+
298+
=== "C++"
299+
300+
```c++
301+
#include <algorithm>
302+
#include <functional>
303+
#include <iostream>
304+
#include <queue>
305+
#include <unordered_set>
306+
#include <vector>
307+
308+
using namespace std;
309+
using LL = long long;
310+
311+
struct Node
312+
{
313+
int i, j, k;
314+
LL val;
315+
bool operator<(const Node &other) const { return val < other.val; }
316+
};
317+
318+
int main()
319+
{
320+
int n;
321+
LL m;
322+
cin >> n >> m;
323+
vector<LL> a(n), b(n), c(n);
324+
for (auto &x : a)
325+
cin >> x;
326+
for (auto &x : b)
327+
cin >> x;
328+
for (auto &x : c)
329+
cin >> x;
330+
sort(a.begin(), a.end(), greater<int>());
331+
sort(b.begin(), b.end(), greater<int>());
332+
sort(c.begin(), c.end(), greater<int>());
333+
unordered_set<LL> vis;
334+
335+
auto tag = [m](int i, int j, int k) -> LL
336+
{ return i * m * m + j * m + k; };
337+
338+
auto val = [&a, &b, &c](int i, int j, int k) -> LL
339+
{ return a[i] * b[j] + a[i] * c[k] + b[j] * c[k]; };
340+
341+
priority_queue<Node> heap;
342+
heap.push({0, 0, 0, val(0, 0, 0)});
343+
vis.insert(tag(0, 0, 0));
344+
for (int s = 1; s < m; s++)
345+
{
346+
auto [i, j, k, v] = heap.top();
347+
heap.pop();
348+
if (i + 1 < n && !vis.count(tag(i + 1, j, k)))
349+
{
350+
heap.push({i + 1, j, k, val(i + 1, j, k)});
351+
vis.insert(tag(i + 1, j, k));
352+
}
353+
if (j + 1 < n && !vis.count(tag(i, j + 1, k)))
354+
{
355+
heap.push({i, j + 1, k, val(i, j + 1, k)});
356+
vis.insert(tag(i, j + 1, k));
357+
}
358+
if (k + 1 < n && !vis.count(tag(i, j, k + 1)))
359+
{
360+
heap.push({i, j, k + 1, val(i, j, k + 1)});
361+
vis.insert(tag(i, j, k + 1));
362+
}
363+
}
364+
cout << heap.top().val;
365+
return 0;
366+
}
367+
```

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
最近更新:
1212

13+
- (20250617) [ABC391(A-F) 题解](./algorithm/AtCoder/abc391.md)
1314
- (20250614) [ABC390(A-F) 题解](./algorithm/AtCoder/abc390.md)
1415
- (20250606) [ABC389(A-F) 题解](./algorithm/AtCoder/abc389.md)
1516
- (20250601) [ABC388(A-G) 题解](./algorithm/AtCoder/abc388.md)

mkdocs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ nav:
99
- 算法:
1010
- algorithm/index.md
1111
- AtCoder:
12+
- ABC391(A-F): algorithm/AtCoder/abc391.md
1213
- ABC390(A-F): algorithm/AtCoder/abc390.md
1314
- ABC389(A-F): algorithm/AtCoder/abc389.md
1415
- ABC388(A-G): algorithm/AtCoder/abc388.md
@@ -65,7 +66,7 @@ nav:
6566
- dev/index.md
6667
- 数据库:
6768
- CMU 15-799 | 优化器:
68-
- System R 论文精读: dev/db/optimizer/SystemR.md
69+
- System R 论文精读: dev/db/optimizer/SystemR/SystemR.md
6970
- CMU 15-445:
7071
- 缓冲池: dev/db/15445/buffer_pool_manager.md
7172
- 常见哈希索引方案介绍: dev/db/15445/hash.md

0 commit comments

Comments
 (0)