G - As far as possible 解説
by
mechanicalpenciI
以下では、与えられる木を、頂点 \(1\) を根とした根付き木として考えます。
まず、高橋君の戦略について考えます。
木の各辺について、その辺で木を \(2\) つに分けた時、頂点 \(1\) を含まない方の部分木に青木君が選んだ頂点が \(1\) つ以上含まれているとき、高橋君はその辺を(往復で) \(2\) 回以上通るような歩道を構成する必要があります。一方で、その条件をみたす最短の歩道、すなわちそのような辺をちょうど \(2\) 回通り、それ以外の辺は一度も通らないような歩道を構成することができます。これは例えば不要な辺を削除した後にオイラーツアーを行うような手法で構成することができます。
これを踏まえて、木DPでこの問題を解くことを考えます。 各頂点 \(v\) を根とした部分木 \(T_v\) について、「\(T_v\)の上でゲームを行いお互いが最善の行動をしたときの、\(K=1,2,\ldots,|T_v|\) におけるスコアの値」\((S_v(1), S_v(2), \ldots, S_v(T_v))\) を求めていくことを考えます。 ただし、\(|T_v|\) で \(T_v\) に含まれる頂点数を表します。
\(T_u\) が\(1\) 頂点のみからなる場合は \(|T_u|=1\), \(S_u(1)=0\) です。
頂点 \(u\) が \(v_1,v_2,\ldots,v_c\) を直接の子として持ち、\(u\) とそれらを結ぶ辺の長さはそれぞれ \(d_1,d_2,\ldots,d_c\) であるとし、\(T_{v_i}\) \((1\leq i\leq c)\) においてのスコアの値はすでに求まっているとします。このとき、\(K<|T_u|\) のときに頂点 \(u\) を選ぶメリットがないことに注意すると、\(S(u,K)\) \((1\leq K < |T_u|)\)は、
\[ S(u,K)=\max_{x_1+x_2+\cdots+x_c=K}\sum_{i=1}^c \begin{cases} 0 & (x_i=0) \\ 2d_i+S(v_i,x_i) & (1\leq x_i\leq |T_i|) \end{cases} \]
で求めることができます。ただし、各 \(x_i\) は \(0\leq x_i\leq |T_i|\) の範囲で動くものとします。最後に、 \(S(u,|T_u|)=S(u,|T_u|-1)\) です。
これは、任意の \(v\) について \(S(v,x)\leq S(v,x+1)\) であることに注意すると、\(c\) 個の列
\((2d_i+S(v_i,1),S(v_i,2)-S(v_i,1),\ldots, S(v_i,|T_i|)-S(v_i,|T_i|-1) )\) のそれぞれから先頭のいくつかの要素を合計 \(K\) 個になるように取り出し、足し合わせたもののうち最大のものと考えることができます。
さらに、これが \(S(v_i,1)\geq S(v_i,2)-S(v_i,1)\) および \(S(v_i,x)-S(v_i,x-1)\geq S(v_i,x+1)-S(v_i,x)\) をみたすとすると、
多重集合 \(\mathcal{S}\) を
\[ \mathcal{S}_u= \left[\bigcup_{i=1}^c \{2d_i+S(v_i,1),S(v_i,2)-S(v_i,1),\ldots, S(v_i,|T_i|)-S(v_i,|T_i|-1) \}\right]\cup\{0\} \]
で定義し、この要素を降順にソートしたものを \((z_1,z_2,\ldots,z_{|T_u|})\) としたとき、これは \((S(u,1),S(u,2)-S(u,1),\ldots, S(u,|T_u|)-S(u,|T_u|-1))\) と一致します。このとき \(u\) についても\(S(u,1)\geq S(u,2)-S(u,1)\) および \(S(u,x)-S(u,x-1)\geq S(u,x+1)-S(u,x)\) が成り立っています。なお、\(|T_u|=1\) のときは自動的に成り立っていることから、これは帰納的に常に成り立り、よってこのように計算することが可能です。
以上を踏まえて、次のように計算することができます。
葉でない各頂点における計算においてその頂点における \(2d_i+S(v_i,1)\) \((1\leq i\leq c)\) のうち最大であるものを \(S(u,1)\) として \(1\) つ保持し、それ以外の\(c-1\) 個および\(1\) つの \(0\) を多重集合 \(\mathcal{S} \) に入れることを繰り返します。
\(S(1,1)\) は最後に保持されている値であり、\(S(1,K)\) は \(S(1,1)\) に \(\mathcal{S}\) のうち大きい方から \(K-1\) 個を加えた値となります。
このとき計算量は \(O(N\log N)\) であり、十分高速です。
よってこの問題を解くことができました。
c++ による実装例:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i, n) for(int i = 0; i < n; ++i)
#define MAX_N (int)2e+5
vector<long long>a;
vector<pair<int,ll> >e[MAX_N];
ll dfs(int k,int p){
ll mx=0,tmp;
int sz=e[k].size();
rep(i,sz){
if(e[k][i].first!=p){
tmp=dfs(e[k][i].first,k)+e[k][i].second;
if(tmp>mx){
a.push_back(mx);
mx=tmp;
}
else a.push_back(tmp);
}
}
return mx;
}
int main(void){
int n,u,v,sz;
ll x;
cin>>n;
rep(i,n-1){
cin>>u>>v>>x;
u--,v--;
e[u].push_back({v,x});
e[v].push_back({u,x});
}
a.push_back(dfs(0,-1));
sort(a.begin(),a.end());
x=0;
sz=a.size();
rep(i,n){
x+=a[sz-i-1]*2;
cout<<x<<endl;
}
return 0;
}
投稿日時:
最終更新:
