ABC187 E - Through Path
けんちょんさんの記事見て解説ACしました。 drken1215.hatenablog.com
解説ACなのですが、身につくことがあったのでメモ記事を書きます。 競プロの問題といたよ履歴もこれで3つ目ですね。章構成のテンプレとか挿入できるようにしておきたい。
問題
(コンテスト中に)考えたこと
制約的に計算量を落とす仕組みが必要。 「木 クエリ」で検索するとセグメント木が出てくる。色々見ているうちに遅延評価セグ木がいいのでは?と思ったりしたけど、セグ木の勉強をちょっとやってギブアップ
解答
木構造のimos法みたいな感じでした。 この問題の発展型みたいな感じらしいです。 atcoder.jp
悩んだポイントとしては、aとbのどっちがとりあえずの根(ここではノード0)に近いか?の判定方法です。 これを知るためにDFSしてたら本末転倒なので…。 これは木構造を根付き木として扱って、出発側の親が行っちゃいけないとこか否か、で判断できるみたいです。確かに…。
#include <bits/stdc++.h> using namespace std; #define rep(i,n) for(long long(i)=0;(i)<(n);(i)++) void dfs_root(vector<vector<long long>>& Graph, vector<long long>&root, vector<bool>&seen, long long cur){ for(auto it : Graph[cur]){ if(!seen[it]){ seen[it] = true; root[it] = cur; dfs_root(Graph, root, seen, it); } } } void dfs_add(vector<vector<long long>>& Graph, vector<long long>&root, vector<bool>&seen, vector<long long> &add,long long cur, long long s){ // current の addを処理 seen[cur] = true; root[cur] = add[cur] + s; for(auto it : Graph[cur]){ if(!seen[it]){ dfs_add(Graph, root, seen, add, it, s + add[cur]); } } } int main(){ long long n; cin >> n; vector<long long> a(n),b(n); vector<vector<long long>> Graph(n); rep(i,n - 1){ cin >> a[i] >> b[i]; a[i]--; b[i]--; Graph[a[i]].push_back(b[i]); Graph[b[i]].push_back(a[i]); } long long q; cin >> q; vector<long long> root(n, -1); vector<bool> seen(n, false); seen[0] = true; dfs_root(Graph, root, seen, 0); vector<long long> add(n,0); rep(i,q){ long long t,e,x; cin >> t >> e >> x; long long A,B; A = a[e - 1]; B = b[e - 1]; if(t == 2) swap(A,B); if(root[A] == B){ add[A] += x; } else { add[0] += x; add[B] -= x; } } rep(i,n) root[i] = 0; rep(i,n) seen[i] = false; seen[0] = true; dfs_add(Graph, root, seen , add, 0, 0); rep(i,n){ cout << root[i] << endl; } return 0; }
学んだこと
- 根付き木において、隣接するノードのどちらが親側に近いかは、ノードの親がそれぞれなにか?を判定すればいい。(親がもう片方のノードになっているやつは、より根から遠い)
- セグ木の勉強をすると良い。(この問題は関係ないけど)
感想
解説を見ると通せないわけでもない、といった気がしてくるので、E問題もちょくちょく解いてみてなれておくと、本番で解けてぐっとレートが上がったりするのでは?と思ったりした。