CS Academy City Attractions
リンク : https://csacademy.com/contest/archive/task/city-attractions/statement/
問題概要
N頂点の木が存在する。それぞれの頂点に美しさA[i]として入力される。d(x,y):=頂点xと頂点yの距離とする。頂点xにいるとき、次に進む場所はA[y]-d(x,y)が最大になる都市である。K回進んだとき、最後の頂点はいくつか。
解説
すぐに全方位木dpっぽいなとは思ったが、MLEがつらすぎた... 全方位木dpとは簡単に説明すると、全頂点を根として一回探索したいときにO(N2)ではなく、O(N)でできる考え方である。一度全方位木dpをして、頂点iにいるときに次に進む場所を計算しておく。これができれば、あとは頂点1から順番に見ていってサイクルが必ず存在するためKをサイクルの大きさで割った余りにして、また前から見ていけばO(N)で答えを求めることができる。
LL n, k, a[300010], h[300010]; vector<LL>g[300010]; Pll nex[300010], dp[300010]; bool f[300010]; void cmp(Pll &x,Pll y) { if (x.fir < y.fir||(x.fir==y.fir&&x.sec>y.sec)) x = y; } void dfs1(LL cur, LL par) { nex[cur] = Pll{ -INF / 2,-1 }; rep(i, g[cur].size()) { LL v = g[cur][i]; if (v == par)continue; dfs1(v, cur); cmp(nex[cur], { nex[v].fir - 1,nex[v].sec }); cmp(nex[cur], { a[v] - 1,v }); } } void dfs2(LL cur, LL par) { if (par != -1) { cmp(dp[cur], { dp[par].fir - 1, dp[par].sec}); cmp(dp[cur], Pll{ a[par] - 1,par }); } Pll mx = Pll{ -INF / 2,-1 }; rep(i, g[cur].size()) { LL v = g[cur][i]; if (v == par)continue; cmp(dp[v], { mx.fir - 2,mx.sec }); cmp(mx, nex[v]); cmp(mx, { a[v],v }); } reverse(g[cur].begin(), g[cur].end()); mx = Pll{ -INF / 2,-1 }; rep(i, g[cur].size()) { LL v = g[cur][i]; if (v == par)continue; cmp(dp[v], { mx.fir - 2,mx.sec}); cmp(mx, nex[v]); cmp(mx, { a[v],v }); } rep(i, g[cur].size()) { LL v = g[cur][i]; if (par == v)continue; dfs2(v, cur); } cmp(dp[cur], nex[cur]); h[cur] = dp[cur].sec; } int main() { cin >> n>>k; rep(i, n) { cin >> a[i]; } rep(i, n - 1) { LL x, y; cin >> x >> y; x--, y--; g[x].push_back(y); g[y].push_back(x); } rep(i, n)dp[i] = Pll{ -INF / 2,-1 }; dfs1(0, - 1); dfs2(0, -1); LL to = 0; while (f[to]==0) { f[to] = 1; to = h[to]; k--; if (k == 0) { cout << to + 1 << endl; return 0; } } rep(i, n)f[i] = 0; LL cnt = 0; while (f[to] == 0) { f[to] = 1; to = h[to]; cnt++; } k %= cnt; while (k > 0)to = h[to], k--; cout << to + 1 << endl; return 0; }