久しぶりです。
yapattaですー。
競技プログラミングの問題でも一つ。
結構面白かったし自分の理解のために説明でもしてみようかと。
問題概要
構造がわからないN頂点を持つ木が用意されている。M個のクエリが実行される。
ここで言うクエリとは(a,b)という形で与えられ、クエリを実行すると頂点aとbを結ぶときに通るパスに書かれた数を+1する。初期状態ですべての辺には0が書かれている。
クエリをすべて実行した後に、すべての辺に書かれた数が2の倍数になるような木の構造が存在するなら"YES"、それ以外は"NO"を出力せよ!という問題。
考えたこと
すべての頂点において、クエリ(a,b)として現れる回数が、偶数個になるとき"YES"となり、一つでも奇数個になるとき"NO"となる。
理由
まず、ここでaとbのLCAをtと、今回の木の根をrと置く。
(a,b)というクエリを、(a,b)を(a,t), (b,t)と置き変えられる。
(LCAとは、根付き木のある二頂点a,bの共通祖先で最も根から離れている頂点)
aとt間の辺には+1, bとt間の辺には+1とする。
これはaとr間、bとr間に+1をすることと同じである。
なぜなら、aとr間、bとr間に+1をすると、tとr間が+2されてしまうが、それぞれの辺に書かれた辺の数が2の倍数になるかさえわかればいいので、+2と+0は同じに見なされるからだ。
次に、親から一番離れている点でかつクエリ(a,b)として現れる回数が奇数個の点に着目する。
このような点を一つでも含む場合、"NO"になる。
このような点をk、kの親をk'と置く。
このようなkが存在する場合、すべての辺に書かれた数が2の倍数になるような木の構造が存在しない。なぜなら、kとk'を結ぶ辺に書かれた数が奇数になるからだ。 また、他のクエリにおける、kとkの親を結ぶ辺に対する加算ではすべて+2される。つまり、(k,k')以外のクエリですべて+2されるから、(k,k')以外による影響を考える必要がない。
上記のような考察が僕の頭では精一杯でした。
ということで、ソースコード!
#include <bits/stdc++.h> using namespace std; int main(){ int n,m; cin >> n >> m; vector<int> edge_num(n,0); for(int i=0;i<m;i++) { int a,b; cin >> a >> b; a--,b--; edge_num[a]++; edge_num[b]++; } for(int i=0;i<n;i++) { if(edge_num[i]%2 == 1) { cout << "NO" << endl; return 0; } } cout << "YES" << endl; return 0; }