ARC063E 木と整数(800)
問題
頂点の木が与えられます。各頂点に整数を1つ書き込んで、隣接する頂点に書き込まれている数字の差がになるようにしたいです。すでに個の頂点に数字が書き込まれているとき、条件を満たすように数字を書き込むことができるか判定してください。書き込むことができるならば、例をひとつ構成してください。
制約
考察
まず、条件を満たすように数字を書き込むことができる必要条件を考えてみます。すでに数字が書き込まれている頂点の集合をとすると、に対し、以下が成り立っていなければなりません。
(1)
(2)
一つ辺をたどるごとに数字は多くとも1しか増減しないので、(1)が必要です。また、一つ辺をたどるごとに書かれている数字の偶奇が変わるので、(2)が必要です。
逆にこれらの条件が成り立っていれば、以下の方法で構築することができます。
1. 新たに頂点を1つ作り(とする)、からにコストの辺を張る
2. からの最短距離を求める(この最短距離を各頂点に書き込む)
このようにして書き込んだ数が条件を満たしていることを確かめましょう。
への最短距離がと一致していることについて
(1)よりが成り立つので、への最短距離はになります。隣接する頂点の最短距離の差が1になることについて
最短距離の性質より、隣接する頂点の最短距離の差は辺の重み(つまり)以下です。さらに(2)より、始点から隣接する頂点へのパスの長さの偶奇が異なることが分かります1。よって隣接する頂点への最短距離の差はになります。
これで、(1)(2)が成り立っているならばこの方法で構築できることが分かりました。また、この方法で構築できなければ(1)または(2)が成り立っていないので、条件を満たす数字の書き込み方は存在しません。よってこの方法で構築してみて、条件を満たしているかどうか確かめることでYesかNoか分かります。2
#include<bits/stdc++.h> using namespace std; using Graph=vector<vector<int>>; using P=pair<int,int>; const int INF=1e9; int main() { int N; cin>>N; Graph G(N); for(int i=0;i<N-1;i++){ int a,b; cin>>a>>b; a--; b--; G[a].push_back(b); G[b].push_back(a); } int K; cin>>K; vector<int> num(N,-1); for(int i=0;i<K;i++){ int v,p; cin>>v>>p; num[v-1]=p; } vector<int> dist(N,INF); priority_queue<P,vector<P>,greater<P>> Q; for(int i=0;i<N;i++) if(num[i]>=0){ dist[i]=num[i]; Q.push({dist[i],i}); } while(!Q.empty()){ int v=Q.top().second; Q.pop(); for(int u:G[v]) if(dist[u]>dist[v]+1){ dist[u]=dist[v]+1; Q.push({dist[u],u}); } } bool ok=true; for(int i=0;i<N;i++){ if(num[i]>=0 and dist[i]!=num[i]) ok=false; for(int j:G[i]) if(dist[i]==dist[j]) ok=false; } cout<<(ok? "Yes":"No")<<endl; if(ok) for(int i=0;i<N;i++) cout<<dist[i]<<endl; return 0; }
まとめ
- 必要条件を考える
- 隣接する頂点の最短距離の差は辺の重み以下
感想
必要条件は分かりましたが、そこから進みませんでした。数字の差が以下⇒最短距離を書き込めば良さそうと反応できれば良かったと思います。