|
| 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 | + ``` |
0 commit comments