nowcoder 树的距离 (DFS序建主席树)

链接:https://ac.nowcoder.com/acm/problem/14415
来源:牛客网

题目描述

wyf非常喜欢树。一棵有根数树上有N个节点,1号点是他的根,每条边都有一个距离,而wyf是个爱问奇怪问题的熊孩子,他想知道对于某个点x,以x为根的子树上,所有与x距离大于等于k的点与x的距离之和。

输入描述:

第一行一个正整数N

接下来N-1描述这棵树,每行两个数第i行两个数p和D表示树上有一条p到i+1长度为D的边。(p<=i)

下面一行一个正整数Q表示wyf的询问次数。

接下来Q行每行两个正整数x和k。 (1<=N,Q<=2x105,1<=D,K<=106)

输出描述:

对于每次询问x,k输出以x为根的子树上,所有与x距离大于等于k的点与x的距离之和。(若不存在这样的点,则输出应为0)

示例1
输入

3
1 2
1 3
2
1 3
1 2

输出

3
5

思路:

子树问题很容易想到通过dfs序转化为序列的区间问题
区间问题很容易想到主席树
所以这题就用dfs序建主席树来解

主席树维护每个节点为根到x的距离dist[x]的cnt和sum

询问就变为:
区间中距离根的距离大于等于dist[x]+k的和减掉cnt*dist[x]

还有就是查询的时候直接查询r[L[x]]到r[R[x]],L[x]不用减1
因为边权附在下面的点上了(如果是点权好像要减)

ps:
因为爆int,所以很多地方要改longlong
但是直接define int longlong 超内存了
然后手动修改的时候会漏掉很多地方(比如mid等等)
结果就是一直wa
有毒

code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define ll long long
const int maxm=2e5+5;
const int M=maxm*2;
int head[maxm],nt[M],to[M],cnt;
ll w[M];
int L[maxm],R[maxm];
ll dist[maxm];
int n,q;
ll ma;//线段树最大范围1-ma
struct Node{
    int lc,rc;
    ll cnt,sum;
}a[maxm*40];
int r[maxm],tot;
void init(){
    memset(head,-1,sizeof head);
    cnt=1;
    tot=0;
}
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void update(int &k,ll x,ll l,ll r,int last){
    k=tot++;
    a[k]=a[last];
    a[k].sum+=x;
    a[k].cnt++;
    if(l==r)return ;
    ll mid=(l+r)/2;
    if(x<=mid)update(a[k].lc,x,l,mid,a[last].lc);
    else update(a[k].rc,x,mid+1,r,a[last].rc);
}
void dfs(int x,int fa,ll val){
    L[x]=++cnt;
    dist[x]=val;
    update(r[cnt],dist[x],1,ma,r[cnt-1]);
    for(int i=head[x];i!=-1;i=nt[i]){
        int v=to[i];
        if(v==fa)continue;
        dfs(v,x,val+w[i]);
    }
    R[x]=cnt;
}
ll anscnt,anssum;
void ask(ll st,ll ed,ll l,ll r,int x,int y){
    if(st<=l&&ed>=r){
        anscnt+=a[y].cnt-a[x].cnt;
        anssum+=a[y].sum-a[x].sum;
        return ;
    }
    ll mid=(l+r)/2;
    if(st<=mid){
        ask(st,ed,l,mid,a[x].lc,a[y].lc);
    }
    if(ed>=mid+1){
        ask(st,ed,mid+1,r,a[x].rc,a[y].rc);
    }
}
signed main(){
    init();
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int p,d;
        scanf("%d%d",&p,&d);
        add(p,i+1,d);
        add(i+1,p,d);
        ma+=d;
    }
    cnt=0;
    dfs(1,-1,0);
    int q;
    scanf("%d",&q);
    while(q--){
        int x;
        ll k;
        scanf("%d%lld",&x,&k);
        k+=dist[x];
        anscnt=anssum=0;
        ask(k,ma,1,ma,r[L[x]],r[R[x]]);
        printf("%lld\n",anssum-anscnt*dist[x]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值