bzoj3653
主席树+dfs序
b在a上方时可以O(1)算出来,子树中就用主席树查询区间和,权值线段树的下标是深度,值是子树size-1,每次查询就行了。。。线段树合并挂了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 3e5 + 5; vector<int> G[N]; int n, cnt, q, tot; int mir[N], root[N * 25], dep[N], lc[N * 25], rc[N * 25], in[N], out[N]; ll sum[N * 25], size[N]; void update(int &x, int y, int l, int r, int p, ll d) { x = ++cnt; sum[x] = sum[y] + d; if(l == r) return; int mid = (l + r) >> 1; lc[x] = lc[y]; rc[x] = rc[y]; if(p <= mid) update(lc[x], lc[y], l, mid, p, d); else update(rc[x], rc[y], mid + 1, r, p, d); } ll query(int x, int y, int l, int r, int a, int b) { if(l > b || r < a) return 0; if(l >= a && r <= b) return sum[y] - sum[x]; int mid = (l + r) >> 1; return (query(lc[x], lc[y], l, mid, a, b) + query(rc[x], rc[y], mid + 1, r, a, b)); } void dfs(int u, int last) { size[u] = 1; in[u] = ++tot; mir[in[u]] = u; for(int i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if(v == last) continue; dep[v] = dep[u] + 1; dfs(v, u); size[u] += size[v]; } out[u] = tot; } int main() { scanf("%d%d", &n, &q); for(int i = 1; i < n; ++i) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dep[1] = 1; dfs(1, 0); for(int i = 1; i <= n; ++i) update(root[i], root[i - 1], 1, n, dep[mir[i]], size[mir[i]] - 1); while(q--) { int x, k; scanf("%d%d", &x, &k); ll ans = query(root[in[x] - 1], root[out[x]], 1, n, dep[x] + 1, min(dep[x] + k, n)) + (ll)min(k, dep[x] - 1) * (ll)(size[x] - 1); printf("%lld\n", ans); } return 0; }