|
| 1 | +## [A - Pairing](https://atcoder.jp/contests/abc378/tasks/abc378_a) |
| 2 | + |
| 3 | +???+ Abstract "题目大意" |
| 4 | + |
| 5 | + 给你 $4$ 个球,第 $i$ 个球的颜色是 $A_i$。你可以执行以下操作任意次: |
| 6 | + |
| 7 | + - 选择两个颜色相同的球,将他们丢弃。 |
| 8 | + |
| 9 | +问:最多能执行几次操作? |
| 10 | + |
| 11 | +??? Success "参考代码" |
| 12 | + |
| 13 | + === "C++" |
| 14 | + |
| 15 | + ```c++ |
| 16 | + #include <iostream> |
| 17 | + #include <unordered_set> |
| 18 | + |
| 19 | + using namespace std; |
| 20 | + |
| 21 | + int main() |
| 22 | + { |
| 23 | + unordered_set<int> set; |
| 24 | + int ans = 0; |
| 25 | + for(int i = 0; i < 4; i++) |
| 26 | + { |
| 27 | + int x; |
| 28 | + cin >> x; |
| 29 | + auto it = set.find(x); |
| 30 | + if(it != set.end()) |
| 31 | + { |
| 32 | + ans++; |
| 33 | + set.erase(it); |
| 34 | + } |
| 35 | + else |
| 36 | + set.insert(x); |
| 37 | + } |
| 38 | + cout << ans << endl; |
| 39 | + return 0; |
| 40 | + } |
| 41 | + ``` |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## [B - Garbage Collection](https://atcoder.jp/contests/abc378/tasks/abc378_b) |
| 46 | + |
| 47 | +???+ Abstract "题目大意" |
| 48 | + |
| 49 | + 某个城市有 $N(1 \le N \le 100)$ 种类型的垃圾,每一种类型的垃圾都有固定的回收时间。第 $i$ 种类型的垃圾的回收时间由 $q_i$ 和 $r_i$ 两个数字描述,表示会在第 $r_i, r_i + q_i, r_i + 2q_i, r_i + 3q_i, ...$ 天的 **晚上** 回收垃圾。其中 $0 \le r_i < q_i \le 10 ^ 9$ |
| 50 | + |
| 51 | + 你需要回答 $Q(1 \le Q \le 100)$ 个询问,每个询问包括两个数字 $t$ 和 $d$,你需要回答:如果在第 $d$ 天的 **白天** 产生了类型为 $t$ 的垃圾,则这个垃圾下一次被回收是在第几天?注意:因为垃圾回收总是在晚上,所有有可能当天的垃圾可以被当天回收。 |
| 52 | + |
| 53 | +??? Note "解题思路" |
| 54 | + |
| 55 | + 求出 $k = \lfloor \frac{d}{q_t} \rfloor$,$x = d \bmod q_t$,如果 $x \le r_t$,则答案就是 $r_t + kq_t$。否则答案就是 $r_t + (k+1)q_t$。 |
| 56 | + |
| 57 | +??? Success "参考代码" |
| 58 | + |
| 59 | + === "C++" |
| 60 | + |
| 61 | + ```c++ |
| 62 | + #include <iostream> |
| 63 | + #include <vector> |
| 64 | + |
| 65 | + using namespace std; |
| 66 | + |
| 67 | + int main() |
| 68 | + { |
| 69 | + int n, m; |
| 70 | + cin >> n; |
| 71 | + vector<int> q(n+1), r(n+1); |
| 72 | + for(int i = 1; i <= n; i++) |
| 73 | + cin >> q[i] >> r[i]; |
| 74 | + cin >> m; |
| 75 | + while(m--) |
| 76 | + { |
| 77 | + int t, d; |
| 78 | + cin >> t >> d; |
| 79 | + int x = d % q[t], k = d / q[t]; |
| 80 | + if(x > r[t]) |
| 81 | + k++; |
| 82 | + cout << r[t] + k * q[t] << endl; |
| 83 | + } |
| 84 | + return 0; |
| 85 | + } |
| 86 | + ``` |
| 87 | + |
| 88 | +--- |
| 89 | + |
| 90 | +## [C - Repeating](https://atcoder.jp/contests/abc378/tasks/abc378_c) |
| 91 | + |
| 92 | +???+ Abstract "题目大意" |
| 93 | + |
| 94 | + 给你一个长度为 $N(2 \le N \le 2 \times 10 ^ 5)$ 的序列 $A = (A_1, A_2, ..., A_N)$。 |
| 95 | + |
| 96 | + 对于每个 $i = 1, 2, ..., N$,回答以下问题: |
| 97 | + |
| 98 | + - 是否存在 $1 \le j < i$ 满足 $A_j = A_i$?如果存在这样的 $j$,输出最大的 $j$。如果不存在这样的 $j$,输出 `-1` |
| 99 | + |
| 100 | +??? Note "解题思路" |
| 101 | + |
| 102 | + 用哈希表存一下每个数字上一次出现的坐标即可。 |
| 103 | + |
| 104 | +??? Success "参考代码" |
| 105 | + |
| 106 | + === "C++" |
| 107 | + |
| 108 | + ```c++ |
| 109 | + #include <iostream> |
| 110 | + #include <unordered_map> |
| 111 | + |
| 112 | + using namespace std; |
| 113 | + |
| 114 | + int main() |
| 115 | + { |
| 116 | + int n; |
| 117 | + cin >> n; |
| 118 | + unordered_map<int, int> last; |
| 119 | + for (int i = 1; i <= n; i++) |
| 120 | + { |
| 121 | + int x; |
| 122 | + cin >> x; |
| 123 | + auto it = last.find(x); |
| 124 | + if(it == last.end()) |
| 125 | + { |
| 126 | + cout << -1 << ' '; |
| 127 | + last[x] = i; |
| 128 | + } |
| 129 | + else |
| 130 | + { |
| 131 | + cout << it->second << ' '; |
| 132 | + it->second = i; |
| 133 | + } |
| 134 | + } |
| 135 | + return 0; |
| 136 | + } |
| 137 | + ``` |
| 138 | + |
| 139 | +--- |
| 140 | + |
| 141 | +## [D - Count Simple Paths](https://atcoder.jp/contests/abc378/tasks/abc378_d) |
| 142 | + |
| 143 | +???+ Abstract "题目大意" |
| 144 | + |
| 145 | + 给你一个 $H \times W(1 \le H, W \le 10)$ 的网格,每个格子要么是 `#` 表示有障碍物,要么是 `.` 表示没有障碍物。 |
| 146 | + |
| 147 | + 问:有多少条满足以下条件的路径? |
| 148 | + |
| 149 | + - 从任意一个没有障碍物的格子出发。 |
| 150 | + - 每一步可以移动到上下左右的相邻格子中。 |
| 151 | + - 不能进入有障碍物的格子。不能进入到已经走过的格子。 |
| 152 | + - 路径长度为 $K$(移动恰好 $K$ 次)。 |
| 153 | + |
| 154 | +??? Note "解题思路" |
| 155 | + |
| 156 | + dfs 模板题。 |
| 157 | + |
| 158 | +??? Success "参考代码" |
| 159 | + |
| 160 | + === "C++" |
| 161 | + |
| 162 | + ```c++ |
| 163 | + #include <iostream> |
| 164 | + #include <string> |
| 165 | + #include <vector> |
| 166 | + |
| 167 | + using namespace std; |
| 168 | + |
| 169 | + const int dr[] = {-1, 1, 0, 0}; |
| 170 | + const int dc[] = {0, 0, -1, 1}; |
| 171 | + |
| 172 | + int main() |
| 173 | + { |
| 174 | + int n, m, k; |
| 175 | + cin >> n >> m >> k; |
| 176 | + vector<string> g(n); |
| 177 | + for (int i = 0; i < n; i++) |
| 178 | + cin >> g[i]; |
| 179 | + int ans = 0; |
| 180 | + |
| 181 | + auto in_graph = [n, m](int r, int c) -> bool |
| 182 | + { return r >= 0 && r < n && c >= 0 && c < m; }; |
| 183 | + |
| 184 | + auto dfs = [&in_graph, &g, &ans, k](auto &self, int r, int c, int dep) -> void |
| 185 | + { |
| 186 | + if (dep == k) |
| 187 | + { |
| 188 | + ans++; |
| 189 | + return; |
| 190 | + } |
| 191 | + g[r][c] = '#'; |
| 192 | + for (int i = 0; i < 4; i++) |
| 193 | + { |
| 194 | + int nr = r + dr[i]; |
| 195 | + int nc = c + dc[i]; |
| 196 | + if (in_graph(nr, nc) && g[nr][nc] == '.') |
| 197 | + self(self, nr, nc, dep + 1); |
| 198 | + } |
| 199 | + g[r][c] = '.'; |
| 200 | + }; |
| 201 | + |
| 202 | + for (int r = 0; r < n; r++) |
| 203 | + for (int c = 0; c < m; c++) |
| 204 | + if (g[r][c] == '.') |
| 205 | + dfs(dfs, r, c, 0); |
| 206 | + cout << ans << endl; |
| 207 | + return 0; |
| 208 | + } |
| 209 | + ``` |
| 210 | + |
| 211 | +--- |
| 212 | + |
| 213 | +## [E - Mod Sigma Problem](https://atcoder.jp/contests/abc378/tasks/abc378_e) |
| 214 | + |
| 215 | +???+ Abstract "题目大意" |
| 216 | + |
| 217 | + 给你一个长度为 $N(1 \le N \le 2 \times 10 ^ 5)$ 的序列 $A = (A_1, A_2, ..., A_N)$ 和一个整数 $M(1 \le M \le 2 \times 10 ^ 5)$。求出 $\sum\limits_{1 \le l \le r \le N}((\sum\limits_{l \le i \le r}A_i) \bmod M)$ |
| 218 | + |
| 219 | +??? Note "解题思路" |
| 220 | + |
| 221 | + 预处理出前缀和,等价于求出 $\sum\limits_{1 \le l \le r \le N}((s[r]-s[l-1]) \bmod M)$,因为要模 $M$,首先将每个前缀和模 $M$ ,这样可以保证 $0 \le s[i] < M$,于是我们可以将模 $M$ 减法进行拆分: |
| 222 | + $$ |
| 223 | + s[r]-s[l-1] = \left\{ |
| 224 | + \begin{matrix} |
| 225 | + s[r] - s[l-1], & \text{if} \ s[l-1] \le s[r] \\ |
| 226 | + s[r] - s[l-1] + M, & \text{if} \ s[l-1] > s[r] |
| 227 | + \end{matrix} |
| 228 | + \right. |
| 229 | + $$ |
| 230 | + 这样,在固定 $r$ 的时候,只需要求出前面有多少个 $s[l-1]$ 比 $s[r]$ 小(树状数组维护每个数字出现的次数即可),同时预处理出前缀和的前缀和,再把剩下的 $M$ 补上就好。 |
| 231 | + |
| 232 | +??? Success "参考代码" |
| 233 | + |
| 234 | + === "C++" |
| 235 | + |
| 236 | + ```c++ |
| 237 | + #include <iostream> |
| 238 | + #include <vector> |
| 239 | + |
| 240 | + using namespace std; |
| 241 | + using LL = long long; |
| 242 | + |
| 243 | + struct FenwickTree |
| 244 | + { |
| 245 | + int n; |
| 246 | + vector<int> t; |
| 247 | + |
| 248 | + FenwickTree(int n) : n(n + 1), t(n + 2) {} |
| 249 | + |
| 250 | + int lowbit(int x) const { return x & -x; } |
| 251 | + |
| 252 | + void add(int x, int k) |
| 253 | + { |
| 254 | + x++; |
| 255 | + for (; x <= n; x += lowbit(x)) |
| 256 | + t[x] += k; |
| 257 | + } |
| 258 | + |
| 259 | + int prefix(int x) const |
| 260 | + { |
| 261 | + x++; |
| 262 | + int sum = 0; |
| 263 | + for (; x; x -= lowbit(x)) |
| 264 | + sum += t[x]; |
| 265 | + return sum; |
| 266 | + } |
| 267 | + |
| 268 | + int query(int l, int r) const { return prefix(r) - prefix(l - 1); } |
| 269 | + }; |
| 270 | + |
| 271 | + int main() |
| 272 | + { |
| 273 | + int n, m; |
| 274 | + cin >> n >> m; |
| 275 | + FenwickTree t(m); |
| 276 | + LL ans = 0, sum = 0; |
| 277 | + LL sr = 0; |
| 278 | + for (int i = 1; i <= n; i++) |
| 279 | + { |
| 280 | + int a; |
| 281 | + cin >> a; |
| 282 | + sr = (sr + a) % m; |
| 283 | + LL cnt = t.query(sr + 1, m); |
| 284 | + ans += i * sr - sum + cnt * m; |
| 285 | + sum += sr; |
| 286 | + t.add(sr, 1); |
| 287 | + } |
| 288 | + cout << ans << endl; |
| 289 | + return 0; |
| 290 | + } |
| 291 | + ``` |
| 292 | + |
| 293 | +--- |
| 294 | + |
| 295 | +## [F - Add One Edge 2](https://atcoder.jp/contests/abc378/tasks/abc378_f) |
| 296 | + |
| 297 | +???+ Abstract "题目大意" |
| 298 | + |
| 299 | + 给你一棵 $N(3 \le N \le 2 \times 10 ^ 5)$ 个点的树,你可以在任意两点间添加恰好一条边,这会导致图中存在一个环。问:有多少种加边的方式可以让恰好添加一条边的图满足以下条件? |
| 300 | + |
| 301 | + - 加边后的图仍然是简单图。 |
| 302 | + - 图中环上所有的点的度数都是 $3$ |
| 303 | + |
| 304 | +??? Note "解题思路" |
| 305 | + |
| 306 | + 加上去的边一定是环上的边,而加边会让两个点的度数都 $+1$,所以一定是在两个度数为 $2$ 的两个点之间加边。 |
| 307 | + |
| 308 | + 先考虑简单的情况,设有一个点 $w$,且 $w$ 有孩子 $u$ 和 $v$,如果 $d[u] = 2$ 且 $d[v] = 2$ 且 $d[w] = 3$,那么就可以连上 $u$ 和 $v$ 的边满足条件。进一步可以发现,如果 $w$ 有 $2$ 个度数为 $2$ 的孩子,就有 $1$ 种连线方案,如果有 $3$ 个度数为 $2$ 的孩子,就有 $3$ 种连线方案。 |
| 309 | + |
| 310 | + 但这种做法还不够完善。一般的,设 $u$ 和 $v$ 的 lca 是 $w$,如果 $w$ 到 $u$ 以及 $w$ 到 $v$ 这两条路径的节点度数都是 $3$,且 $d[u] = 2$ 且 $d[v] = 2$,连上 $u$ 和 $v$ 的边也是满足条件的。为了处理这种情况,只需要把所有度数为 $3$ 且相邻的点合并成 $1$ 个,然后枚举这些度数为 $3$ 的节点有多少个度数为 $2$ 的孩子就可以。 |
| 311 | + |
| 312 | +??? Success "参考代码" |
| 313 | + |
| 314 | + === "C++" |
| 315 | + |
| 316 | + ```c++ |
| 317 | + #include <iostream> |
| 318 | + #include <vector> |
| 319 | + |
| 320 | + using namespace std; |
| 321 | + using LL = long long; |
| 322 | + |
| 323 | + int main() |
| 324 | + { |
| 325 | + int n; |
| 326 | + cin >> n; |
| 327 | + vector<vector<int>> edge(n + 1); |
| 328 | + vector<int> deg(n + 1); |
| 329 | + for (int i = 0; i < n - 1; i++) |
| 330 | + { |
| 331 | + int u, v; |
| 332 | + cin >> u >> v; |
| 333 | + edge[u].push_back(v); |
| 334 | + edge[v].push_back(u); |
| 335 | + deg[u]++; |
| 336 | + deg[v]++; |
| 337 | + } |
| 338 | + vector<int> p(n + 1); |
| 339 | + for(int u = 1; u <= n; u++) |
| 340 | + p[u] = u; |
| 341 | + |
| 342 | + auto dfs_for_merge = [&edge, &p, °](auto &self, int u, int fa) -> void |
| 343 | + { |
| 344 | + for(auto v : edge[u]) |
| 345 | + { |
| 346 | + if(v == fa) |
| 347 | + continue; |
| 348 | + if(deg[u] == 3 && deg[v] == 3) |
| 349 | + p[v] = p[u]; |
| 350 | + self(self, v, u); |
| 351 | + } |
| 352 | + }; |
| 353 | + |
| 354 | + dfs_for_merge(dfs_for_merge, 1, 0); |
| 355 | + vector<int> two_deg_child_cnt(n + 1); |
| 356 | + for(int u = 1; u <= n; u++) |
| 357 | + { |
| 358 | + int pu = p[u]; |
| 359 | + if(pu != u) |
| 360 | + { |
| 361 | + for(auto v : edge[u]) |
| 362 | + if(deg[v] == 2) |
| 363 | + two_deg_child_cnt[pu]++; |
| 364 | + } |
| 365 | + else if(deg[u] == 3) |
| 366 | + { |
| 367 | + for(auto v : edge[u]) |
| 368 | + if(deg[v] == 2) |
| 369 | + two_deg_child_cnt[u]++; |
| 370 | + } |
| 371 | + } |
| 372 | + LL ans = 0; |
| 373 | + for(int u = 1; u <= n; u++) |
| 374 | + { |
| 375 | + LL c = two_deg_child_cnt[u]; |
| 376 | + ans += c * (c - 1) / 2; |
| 377 | + } |
| 378 | + cout << ans << endl; |
| 379 | + return 0; |
| 380 | + } |
| 381 | + ``` |
| 382 | + |
| 383 | +--- |
| 384 | + |
0 commit comments