UVaデビューです。
重心分解。問題はどうやってsubtree間のpathを高速に処理するか。
まず各subtreeの重心からのpathについてdamageとlengthの合計を求めておく。
それをすべて一つの配列に入れてsortして、その配列と同じ大きさのsegtreeにソート後のlengthを入れておく。
segtreeは区間のmaxと、ある値のupdateをできるものを用意する。
さらにsubtreeのpathを順番に見ていき、許容されるdamage内でlengthが最大のものをsegtreeから求める。
この時、同じsubtree内のpathを参照するとダメなので、見ているpathに相当するsegtreeの値を0にしておく。
一発でsample合って、これは来たか?と思ったが結局3WA。
POJと違っていつものテンプレート使えるからよろしい。
namespace CD { //centroid decomposition struct edge { int to, damage, length; }; vector<edge> G[MAX_N]; bool centroid[MAX_N]; int subtree_size[MAX_N]; void init(int n) { rep(i, 0, n) G[i].clear(); } void add_edge(int a, int b, int d, int c) { G[a].push_back((edge){b, d, c}); G[b].push_back((edge){a, d, c}); } int compute_subtree_size(int v, int p) { int c = 1; rep(i, 0, (int)G[v].size()) { int w = G[v][i].to; if(w == p || centroid[w]) continue; c += compute_subtree_size(G[v][i].to, v); } subtree_size[v] = c; return c; } pi search_centroid(int v, int p, int t) { pi res = pi(inf, -1); int s = 1, m = 0; rep(i, 0, (int)G[v].size()) { int w = G[v][i].to; if(w == p || centroid[w]) continue; MIN(res, search_centroid(w, v, t)); MAX(m, subtree_size[w]); s += subtree_size[w]; } MAX(m, t - s); MIN(res, pi(m, v)); return res; } void update(int s, int n); void solve_subproblem(int v) { compute_subtree_size(v, -1); int s = search_centroid(v, -1, subtree_size[v]).sec; centroid[s] = true; int cnt = 0; rep(i, 0, (int)G[s].size()) { if(centroid[G[s][i].to]) continue; solve_subproblem(G[s][i].to); cnt++; } update(s, cnt); centroid[s] = false; } ////////////////////////////////////////////// edit from here //distance from centroid void enumerate_pathes(int v, int p, int d, int l, int cnt, vector<ppi> &ds) { ds.push_back(make_pair(pi(d, l), cnt)); rep(i, 0, (int)G[v].size()) { int w = G[v][i].to; if(w == p || centroid[w]) continue; enumerate_pathes(w, v, d + G[v][i].damage, l + G[v][i].length, cnt, ds); } } int fd(const vector<ppi>& v, int n) { int lv = -1, rv = (int)v.size(); while(rv - lv > 1) { int m = (lv + rv) / 2; if(v[m].fst.fst > n) rv = m; else lv = m; } return rv; } void update(int s, int n) { vector<ppi> ds; vector<vector<ppi>> tds(n); vector<vi> at(n); int cnt = 0; rep(i, 0, (int)G[s].size()) { if(centroid[G[s][i].to]) continue; tds[cnt].push_back(make_pair(pi(0, 0), cnt)); //debug("omo", G[s][i].damage, G[s][i].length); enumerate_pathes(G[s][i].to, s, G[s][i].damage, G[s][i].length, cnt, tds[cnt]); ds.insert(ds.end(), tds[cnt].begin(), tds[cnt].end()); cnt++; } int m = (int)ds.size(); segtree seg; seg.init(m); sort(all(ds)); rep(i, 0, m) { at[ds[i].sec].push_back(i); seg.update(i, ds[i].fst.sec); } //debug(s, ds); //seg.show(); rep(i, 0, n) { rep(j, 0, (int)at[i].size()) { seg.update(at[i][j], 0); } rep(j, 0, (int)at[i].size()) { ppi pp = ds[at[i][j]]; pi p = pp.fst; int at = fd(ds, M - p.fst); int res = seg.get(0, at).v; //debug(p.fst, M - p.fst, at, p.sec, res); MAX(ans, p.sec + res); } rep(j, 0, (int)at[i].size()) { seg.update(at[i][j], ds[at[i][j]].fst.sec); } } } } void solve() { cin >> Q; rep(q, 1, Q + 1) { cin >> N >> M; CD::init(N); rep(i, 0, N - 1) { int a, b, d, c; cin >> a >> b >> d >> c; a--; b--; CD::add_edge(a, b, d, c); } ans = 0; CD::solve_subproblem(0); cout << "Case " << q << ": " << ans << "\n"; } }
解いているときふとmap使えそうだなと思ったけど、使い方がよくわからず結局segtreeで解いてしまった。
イテレーターのスマートな使い方とか習得したい。