C++の練習を兼ねて, AtCoder Beginner Contest 221 の 問題F (Diameter set) を解いてみた.
■感想.
1. 問題Fは, 方針が見えなかったので, 解説を参考にして, ようやく, AC版に到達出来た.
2. 幅優先探索の復習が出来たので, 非常に良かったと思う.
3. 引き続き, 時間を見つけて, 過去問の学習を進めていきたいと思う.
本家のサイト AtCoder Beginner Contest 221 解説 の 各リンク を ご覧下さい.
■C++版プログラム(問題F/AC版).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
// C++(GCC 9.2.1) // 解き直し. // https://atcoder.jp/contests/abc221/editorial/2723 #include <bits/stdc++.h> using namespace std; using LL = long long; using vs = vector<set<int>>; #define repex(i, a, b, c) for(int i = a; i < b; i += c) #define repx(i, a, b) repex(i, a, b, 1) #define rep(i, n) repx(i, 0, n) #define repr(i, a, b) for(int i = a; i >= b; i--) #define pb push_back const LL MOD = 998244353; int main(){ // 1. 入力情報. int N; scanf("%d", &N); vs G(N); rep(i, N - 1){ int u, v; scanf("%d %d", &u, &v); u--; v--; G[u].insert(v); G[v].insert(u); } // 2. bfs(最短距離). // https://ja.wikipedia.org/wiki/幅優先探索 auto bfs = [&](vs &G, int s, int* d){ // 空のキュー. queue<int> q; // 探索地点 s をキュー q に追加. q.push(s); while(!q.empty()){ // キューから取り出す. int u = q.front(); q.pop(); // 取り出した要素を処理. for(auto &e : G[u]){ // 未訪問の頂点を処理. if(d[e]) continue; if(e != s) d[e] = d[u] + 1, q.push(e); } } return; }; // 3. bfs(連結成分). // https://ja.wikipedia.org/wiki/幅優先探索 auto bfs2 = [&](vs &G, int s, int* c, int l){ // 空のキュー. queue<int> q; // 連結成分番号を設定. c[s] = l; // 探索地点 s をキュー q に追加. q.push(s); while(!q.empty()){ // キューから取り出す. int u = q.front(); q.pop(); // 取り出した要素を処理. for(auto &e : G[u]){ // 未訪問の頂点を処理. if(c[e]) continue; if(e != s) c[e] = l, q.push(e); } } return; }; // 4. 直径 の 両端 の 頂点 x, y は? // 4-1. 頂点 1 から 各頂点までの距離を保存. int d1[N], dx[N], dy[N]; rep(i, N) d1[i] = dx[i] = dy[i] = 0; bfs(G, 0, d1); // 4-2. 頂点 1 から 最も遠い 頂点 x を保存. int x = -1, r = 0; rep(i, N){ if(r < d1[i]){ r = max(r, d1[i]); x = i; } } assert(x != -1); // 4-3. 頂点 x から 各頂点までの距離を保存. bfs(G, x, dx); // 4-4. 頂点 x から 最も遠い 頂点 y を保存. int y = -1; r = 0; rep(i, N){ if(r < dx[i]){ r = max(r, dx[i]); y = i; } } assert(y != -1); // 4-5. 頂点 y から 各頂点までの距離を保存. bfs(G, y, dy); // 4-6. 直径 D は? int D = dx[y]; // 5. 直径の偶奇に応じて, 場合の数を計算. // 5-1. 直径 D が 奇数. // ex. // 2 // 1 2 // -> 1 が 正解のはず. // -> hand_00.txt で, WA版 だったため, ロジック修正. LL ans = 1; if(D & 1){ // 頂点 A, B を 抽出. int a = -1, b = -1; int D1 = (D - 1) / 2; int D2 = (D + 1) / 2; rep(i, N){ if(dx[i] == D1 && dy[i] == D2) a = i; if(dx[i] == D2 && dy[i] == D1) b = i; } assert(a != -1); assert(b != -1); // グラフから, 辺 (a, b) を 除外. G[a].erase(b); G[b].erase(a); // 頂点 A から 各頂点までの距離を保存. int da[N], db[N]; rep(i, N) da[i] = db[i] = 0; bfs(G, a, da); // 頂点 B から 各頂点までの距離を保存. bfs(G, b, db); // 頂点 A から, 距離 (D - 1) / 2 の 頂点数. LL m1 = 0; rep(i, N) if(da[i] == D1 && i != a) m1++; // 頂点 B から, 距離 (D - 1) / 2 の 頂点数. LL m2 = 0; rep(i, N) if(db[i] == D1 && i != b) m2++; // 場合の数を集計. ans = m1 * m2 % MOD; } // 5-2. 直径 D が 偶数. if(!(D & 1)){ // 頂点 C を 抽出. int c = -1; int D3 = D / 2; rep(i, N){ if(dx[i] == D3 && dy[i] == D3){ c = i; break; } } assert(c != -1); // 頂点 C から 各頂点までの距離を保存. int dc[N]; rep(i, N) dc[i] = 0; bfs(G, c, dc); // グラフから, 頂点 C と 繋がっている辺を削除. G[c].clear(); rep(i, N) G[i].erase(c); // 部分木 に 分割. int connection[N], idx = 0; rep(i, N) connection[i] = 0; rep(i, N) if(!connection[i]) bfs2(G, i, connection, ++idx); // 部分木 ごとに m[i] を 計算. // 頂点 C からの距離が, D / 2 であるものを集計. LL m[idx + 1]; rep(i, idx + 1) m[i] = 0; rep(i, N) if(dc[i] == D3) m[connection[i]]++; // 場合の数を集計. repx(i, 1, idx + 1){ ans *= (m[i] + 1); ans %= MOD; } repx(i, 1, idx + 1){ ans += MOD; ans -= m[i]; } ans += MOD; ans--; ans %= MOD; } // 6. 出力. printf("%lld\n", ans); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
[入力例] 5 1 2 1 3 1 4 4 5 [出力例] 2 ※AtCoderのテストケースより [入力例] 4 1 2 1 3 1 4 [出力例] 4 ※AtCoderのテストケースより [入力例] 2 1 2 [出力例] 1 [入力例] 10 1 2 1 3 1 4 3 5 3 6 2 9 2 10 4 7 4 8 [出力例] 20 [入力例] 12 1 2 2 3 3 4 3 5 5 10 5 12 2 9 2 8 1 11 1 6 1 7 [出力例] 6 [入力例] 20 1 3 3 6 3 5 3 2 3 4 5 11 5 12 5 19 4 9 4 10 4 17 2 7 2 8 2 18 1 15 1 16 6 13 6 14 6 20 [出力例] 753 |
■参照サイト
AtCoder Beginner Contest 221