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;
}
View Code

 

posted @ 2017-10-31 20:16  19992147  阅读(182)  评论(0编辑  收藏  举报