結果
問題 | No.2618 除霊 |
ユーザー |
![]() |
提出日時 | 2022-02-06 17:15:14 |
言語 | C++14 (gcc 13.3.0 + boost 1.87.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 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 43 |
コンパイルメッセージ
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 Treestruct 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の隣接頂点nvif (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;}