1. 程式人生 > >Codeforces #447 Div2 D

Codeforces #447 Div2 D

push 所有 ack out namespace begin mar ace code

#447 Div2 D

題意

給一棵完全二叉樹,每條邊有權值為兩點間的距離,每次詢問 \(x, h\) ,從結點 \(x\) 出發到某一結點的最短路的距離 \(d\) 如果小於 \(h\) ,則答案加上 \(h - d\) ,考慮所有結點並輸出答案。

分析

通過建樹過程可以發現這是一棵完全二叉樹,也就是說樹很矮。
可以預處理這棵樹,對於每一個結點,我們可以計算出以這個結點為根結點的子樹中的所有結點到當前子樹的根結點的距離,從根結點向下 DFS 即可,然後自下而上合並,類似歸並排序合並的過程。再預處理下前綴和,這樣就很容易求得子樹中有多少結點到根結點的距離小於 \(h\) ,向上走 \(log(n)\)

次一定可以到根結點。

完全二叉樹 很矮!祖先結點很少!很多情況都可以遍歷(暴力)祖先結點!

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int f, s, mx;
int n, m, a[N], l[N], r[N];
vector<int> G[N];
vector<ll> S[N];
void mergeUp(int rt, int cl, int cr) {
    int i = 0, j = 0;
    while
(i < cl || j < cr) { if(i == cl) G[rt].push_back(r[j++]); else if(j == cr) G[rt].push_back(l[i++]); else { if(l[i] < r[j]) G[rt].push_back(l[i++]); else G[rt].push_back(r[j++]); } } ll sum = 0; for(int i = 0; i < G[rt].size(); i++) { sum += G[rt][i]; S[rt].push_back(sum); } } void
build(int rt) { if(rt >= s - mx + 1) return; build(rt * 2); build(rt * 2 + 1); int cl = 0, cr = 0; if(rt * 2 <= n) for(int i = 0; i < G[rt * 2].size(); i++) { l[cl++] = G[rt * 2][i] + a[rt * 2]; } if(rt * 2 + 1 <= n) for(int i = 0; i < G[rt * 2 + 1].size(); i++) { r[cr++] = G[rt * 2 + 1][i] + a[rt * 2 + 1]; } if(cl + cr > 0) mergeUp(rt, cl, cr); } int main() { scanf("%d%d", &n, &m); s = 1, mx = 1, f = 0; while(s < n) { mx *= 2; s += mx; f++; } for(int i = 1; i < n; i++) { int x; scanf("%d", &x); a[i + 1] = x; G[i].push_back(0); } G[n].push_back(0); build(1); while(m--) { int now, h, pre = -1; scanf("%d%d", &now, &h); ll ans = h; while(now) { if(now != 1 && a[now] < h) ans += h - a[now]; if(pre == -1) { int pos = lower_bound(G[now].begin(), G[now].end(), h) - G[now].begin() - 1; if(pos > 0) ans += 1LL * pos * h - S[now][pos]; } else { if(now * 2 == pre && now * 2 + 1 <= n) { int nxt = now * 2 + 1; int pos = lower_bound(G[nxt].begin(), G[nxt].end(), h - a[nxt]) - G[nxt].begin() - 1; if(pos > 0) ans += 1LL * pos * (h - a[nxt]) - S[nxt][pos]; if(a[nxt] < h) ans += h - a[nxt]; } else if(now * 2 + 1 == pre && now * 2 <= n) { int nxt = now * 2; int pos = lower_bound(G[nxt].begin(), G[nxt].end(), h - a[nxt]) - G[nxt].begin() - 1; if(pos > 0) ans += 1LL * pos * (h - a[nxt]) - S[nxt][pos]; if(a[nxt] < h) ans += h - a[nxt]; } } h -= a[now]; pre = now; now /= 2; } cout << ans << endl; } return 0; }

Codeforces #447 Div2 D