結果
問題 | No.2618 除霊 |
ユーザー | startcpp |
提出日時 | 2022-02-06 17:15:14 |
言語 | C++14 (gcc 12.3.0 + boost 1.83.0) |
結果 |
AC
|
実行時間 | 515 ms / 2,000 ms |
コード長 | 9,647 bytes |
コンパイル時間 | 1,398 ms |
コンパイル使用メモリ | 109,764 KB |
実行使用メモリ | 43,176 KB |
最終ジャッジ日時 | 2024-06-28 22:29:53 |
合計ジャッジ時間 | 23,819 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge2 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 7 ms
17,408 KB |
testcase_01 | AC | 5 ms
17,408 KB |
testcase_02 | AC | 7 ms
17,408 KB |
testcase_03 | AC | 6 ms
17,408 KB |
testcase_04 | AC | 7 ms
17,408 KB |
testcase_05 | AC | 6 ms
17,536 KB |
testcase_06 | AC | 430 ms
40,376 KB |
testcase_07 | AC | 428 ms
40,264 KB |
testcase_08 | AC | 449 ms
40,724 KB |
testcase_09 | AC | 445 ms
40,288 KB |
testcase_10 | AC | 467 ms
40,776 KB |
testcase_11 | AC | 421 ms
40,664 KB |
testcase_12 | AC | 460 ms
40,700 KB |
testcase_13 | AC | 430 ms
42,984 KB |
testcase_14 | AC | 459 ms
42,300 KB |
testcase_15 | AC | 405 ms
42,340 KB |
testcase_16 | AC | 408 ms
42,416 KB |
testcase_17 | AC | 440 ms
43,168 KB |
testcase_18 | AC | 455 ms
42,236 KB |
testcase_19 | AC | 410 ms
43,176 KB |
testcase_20 | AC | 391 ms
42,528 KB |
testcase_21 | AC | 426 ms
42,048 KB |
testcase_22 | AC | 469 ms
43,144 KB |
testcase_23 | AC | 423 ms
43,164 KB |
testcase_24 | AC | 429 ms
42,588 KB |
testcase_25 | AC | 453 ms
43,080 KB |
testcase_26 | AC | 450 ms
42,016 KB |
testcase_27 | AC | 396 ms
42,628 KB |
testcase_28 | AC | 415 ms
43,104 KB |
testcase_29 | AC | 456 ms
43,176 KB |
testcase_30 | AC | 488 ms
43,164 KB |
testcase_31 | AC | 515 ms
40,808 KB |
testcase_32 | AC | 434 ms
41,164 KB |
testcase_33 | AC | 441 ms
41,292 KB |
testcase_34 | AC | 418 ms
40,208 KB |
testcase_35 | AC | 459 ms
40,728 KB |
testcase_36 | AC | 493 ms
41,044 KB |
testcase_37 | AC | 464 ms
40,652 KB |
testcase_38 | AC | 488 ms
41,284 KB |
testcase_39 | AC | 508 ms
41,268 KB |
testcase_40 | AC | 458 ms
40,504 KB |
testcase_41 | AC | 470 ms
41,156 KB |
testcase_42 | AC | 458 ms
40,968 KB |
コンパイルメッセージ
In member function 'void FastSolver::add_near(int)', inlined from 'virtual std::vector<int> FastSolver::solve(Testcase&)' at main.cpp:407:14: main.cpp:338:21: warning: 'x' may be used uninitialized [-Wmaybe-uninitialized] 338 | int bfs_v = to_bfs[v]; | ^~~~~ main.cpp: In member function 'virtual std::vector<int> FastSolver::solve(Testcase&)': main.cpp:398:37: note: 'x' was declared here 398 | int x; | ^
ソースコード
//3解法間のストレステスト #include <iostream> #include <string> #include <vector> #include <queue> #include <random> using namespace std; mt19937 mt(252521); //乱数生成器, メルセンヌ・ツイスタ //Union Find Tree struct UF { int n; vector<int> par; int merge_cnt; void init(int n) { this->n = n; par.resize(n); for (int i = 0; i < n; i++) { par[i] = i; } merge_cnt = 0; } int root(int x) { if (par[x] == x) { return x; } return par[x] = root(par[x]); } bool same(int x, int y) { return root(x) == root(y); } void merge(int x, int y) { x = root(x); y = root(y); if (x != y) { par[x] = y; merge_cnt++; } } }; //テストケース構造体 class Testcase { private: vector<int> a, b; public: int n; vector<vector<int>> edge_to; string color; void generate(int n) { UF uf; uf.init(n); edge_to.resize(n); this->n = n; while (uf.merge_cnt < n - 1) { int u = mt() % n; int v = mt() % n; if (uf.same(u, v) == false) { uf.merge(u, v); a.push_back(u); b.push_back(v); edge_to[u].push_back(v); edge_to[v].push_back(u); } } for (int i = 0; i < n; i++) { if (mt() % 100 < 50) { color += 'b'; } else { color += 'w'; } } } void input() { cin >> n; edge_to.resize(n); for (int i = 0; i < n - 1; i++) { int a, b; cin >> a >> b; a--; b--; edge_to[a].push_back(b); edge_to[b].push_back(a); } int m; cin >> m; color = ""; for (int i = 0; i < n; i++) color += 'w'; for (int i = 0; i < m; i++) { int v; cin >> v; v--; color[v] = 'b'; } } void print() { cout << n << endl; for (int i = 0; i < n - 1; i++) { cout << a[i] << " " << b[i] << endl; } cout << color << endl; } }; //Solverの基底クラス class Solver { protected: int n; vector<int> edge_to[200000]; string color; void input(Testcase &testcase) { n = testcase.n; for (int i = 0; i < n; i++) { edge_to[i] = testcase.edge_to[i]; } color = testcase.color; } virtual vector<int> solve(Testcase &testcase) = 0; }; //ナイーブ解1:O(N^2) class NaiveSolver1 : public Solver { private: vector<int> operate(int v) { vector<int> blacks; if (color[v] == 'b') { blacks.push_back(v); } for (int nv : edge_to[v]) { //vの隣接頂点nv if (color[nv] == 'b') { blacks.push_back(nv); } } for (int b : blacks) { color[b] = 'w'; } return blacks; } bool is_danger(int v) { if (color[v] == 'b') { return true; } for (int nv : edge_to[v]) { if (color[nv] == 'b') { return true; } } return false; } int calc() { int ret = 0; for (int v = 0; v < n; v++) { //頂点vについて危険かを調べる if (is_danger(v)) { ret++; } } return ret; } void revert(vector<int> change_vs) { for (int v : change_vs) { color[v] = 'b'; } } public: vector<int> solve(Testcase &testcase) { input(testcase); vector<int> ans; for (int v = 0; v < n; v++) { vector<int> change_vs = operate(v); //色を変更した頂点リストを返す ans.push_back(calc()); revert(change_vs); } return ans; } }; //ナイーブ解2: O(N^2), ランダムグラフのとき高速 class NaiveSolver2 : public Solver { private: int cnt_black[200000]; //cnt_black[v] = 頂点v自身・vの隣接頂点のうち黒であるものの個数 int safe_cnt; //safe_cnt = cnt_black[v]が0であるような頂点vの個数 void calc_init() { safe_cnt = 0; for (int v = 0; v < n; v++) { cnt_black[v] = 0; if (color[v] == 'b') { cnt_black[v]++; } for (int nv : edge_to[v]) { if (color[nv] == 'b') { cnt_black[v]++; } } if (cnt_black[v] == 0) { safe_cnt++; } } } vector<int> operate(int v) { vector<int> blacks; if (color[v] == 'b') { blacks.push_back(v); } for (int nv : edge_to[v]) { if (color[nv] == 'b') { blacks.push_back(nv); } } for (int u : blacks) { //頂点uについて、uから距離1以内の頂点xのcnt_black[x]を1減らす cnt_black[u]--; if (cnt_black[u] == 0) { safe_cnt++; } for (int x: edge_to[u]) { cnt_black[x]--; if (cnt_black[x] == 0) { safe_cnt++; } } } return blacks; } void revert(vector<int> change_vs) { for (int v : change_vs) { if (cnt_black[v] == 0) { safe_cnt--; } cnt_black[v]++; for (int x : edge_to[v]) { if (cnt_black[x] == 0) { safe_cnt--; } cnt_black[x]++; } } } public: vector<int> solve(Testcase &testcase) { input(testcase); calc_init(); vector<int> ans; for (int v = 0; v < n; v++) { vector<int> change_vs = operate(v); ans.push_back(n - safe_cnt); revert(change_vs); } return ans; } }; //想定解: O(N), デバッグ済み class FastSolver : public Solver { private: int black[200000]; //black[v] = vと隣接する黒頂点の個数 int to_bfs[200000]; int parent_bfs[200000]; int child_bfs_l[200000], child_bfs_r[200000]; //[l, r] int safe_bfs[200001]; //safe_bfs[bfsId] = BFS順序でbfsId番の頂点について操作をおこなった場合の、危険でない頂点の個数 void init_member() { for (int i = 0; i < n; i++) { black[i] = 0; to_bfs[i] = 0; parent_bfs[i] = 0; child_bfs_l[i] = 0; child_bfs_r[i] = 0; safe_bfs[i] = 0; } safe_bfs[n] = 0; } void bfs(int root_v) { vector<bool> used(n, false); queue<int> que; int t = 0; for (int bfs_v = 0; bfs_v < n; bfs_v++) { parent_bfs[bfs_v] = -1; child_bfs_l[bfs_v] = -1; child_bfs_r[bfs_v] = -1; } to_bfs[root_v] = t; t++; que.push(root_v); used[root_v] = true; while (!que.empty()) { int v = que.front(); que.pop(); for (int nv : edge_to[v]) { if (used[nv]) { continue; } to_bfs[nv] = t; parent_bfs[t] = to_bfs[v]; if (child_bfs_l[to_bfs[v]] == -1) { child_bfs_l[to_bfs[v]] = t; } //最初見る子のbfsIdになる child_bfs_r[to_bfs[v]] = t; //最後見る子のbfsIdになる t++; que.push(nv); used[nv] = true; } } } //safe_bfs[l, r) += 1, を階差数列上でおこなう void add(int l, int r) { if (debug_flag) { printf("safe_bfs[%d, %d] += 1\n", l, r); } safe_bfs[l]++; safe_bfs[r + 1]--; } void add_near(int v) { int bfs_v = to_bfs[v]; int l = child_bfs_l[bfs_v]; int r = child_bfs_r[bfs_v]; int p = parent_bfs[bfs_v]; //v自身 add(bfs_v, bfs_v); //vの子[l, r] (vが葉でないとき) if (l != -1) { add(l, r); } //vの親(vが根でないとき) if (p != -1) { add(p, p); } } public: bool debug_flag; FastSolver() { debug_flag = false; } vector<int> solve(Testcase &testcase) { input(testcase); init_member(); for (int v = 0; v < n; v++) { for (int nv : edge_to[v]) { if (color[nv] == 'b') { black[v]++; } } } bfs(0); if (debug_flag) { cout << " id | to_bfs, parent_bfs, child_bfs_l, child_bfs_r" << endl; cout << "--------------------------------------------------" << endl; for (int v = 0; v < n; v++) { printf("%3d | %6d, %10d, %11d, %11d\n", v, to_bfs[v], parent_bfs[to_bfs[v]], child_bfs_l[to_bfs[v]], child_bfs_r[to_bfs[v]]); } } for (int v = 0; v < n; v++) { //頂点vが安全になる操作uをカウントアップ if (debug_flag) { printf("\n"); printf("black[%d] = %d, color[%d] = %c\n", v, black[v], v, color[v]); } if (black[v] == 0) { if (color[v] == 'w') { //u: 全頂点, imos法で区間[0, n)に加算 add(0, n - 1); } else { add_near(v); //v自身とvの隣接頂点に加算 } } else if (black[v] == 1) { int x; for (int nv : edge_to[v]) { //v=0,..,Nにおけるループ回数の和は次数の和 = 2N - 2. if (color[nv] == 'b') { x = nv; break; } } if (color[v] == 'w') { add_near(x); //x自身とxの隣接頂点に加算、ここの高速化がデカい! } else { int bfs_x = to_bfs[x]; int bfs_v = to_bfs[v]; add(bfs_x, bfs_x); add(bfs_v, bfs_v); } } else { //操作uで頂点vを安全にするには、u = vとするしかない! int bfs_v = to_bfs[v]; add(bfs_v, bfs_v); } } //累積和を取る for (int i = 0; i < n; i++) { safe_bfs[i + 1] += safe_bfs[i]; } //答えを出力する if (debug_flag) { puts(""); } vector<int> ans; for (int v = 0; v < n; v++) { if (debug_flag) { printf("safe_bfs[%d] = %d\n", v, safe_bfs[v]); } int bfs_v = to_bfs[v]; ans.push_back(n - safe_bfs[bfs_v]); } return ans; } }; NaiveSolver1 solver1; NaiveSolver2 solver2; FastSolver solver3; bool stress_test(int caseNum, int N, Testcase &return_case) { for (int T = 0; T < caseNum; T++) { int n = N; Testcase testcase; testcase.generate(n); vector<int> ans1 = solver1.solve(testcase); vector<int> ans3 = solver3.solve(testcase); if (ans1 != ans3) { cout << "Wrong Answer[" << T << "]" << endl; cout << "Testcase (0-idx):" << endl; testcase.print(); cout << endl; cout << "Different Answer (0-idx):" << endl; for (int i = 0; i < n; i++) { if (ans1[i] != ans3[i]) { cout << i << " : " << ans1[i] << ", " << ans3[i] << endl; } } return_case = testcase; return true; } } cout << "Accepted" << endl; return false; } int main() { /*Testcase testcase; bool is_wrong = stress_test(1000, 15, testcase); if (is_wrong) { solver3.debug_flag = true; solver3.solve(testcase); }*/ Testcase testcase; testcase.input(); vector<int> ans = solver3.solve(testcase); for (int i = 0; i < ans.size(); i++) cout << ans[i] << endl; }