newcoder Wannafly挑战赛4 树的距离

https://www.nowcoder.com/acm/contest/35/D

假设要查询x的子树中,与x的距离>=y的距离和

那么如果有这么一个

由x的子树中的点到x的距离构成的序列,且按距离排好序,

那么在这个序列中,y之后的距离和就是答案

得到子树中按距离排好序的一段序列

按dfs序建立主席树即可

每次查询抽出x的子树序列,

注意主席树的权值是按点到根节点的距离

所以 查询dis[x]+y之后的距离和,再减去 x子树大小*dis[x]

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 200001

typedef long long LL;

int n;

int front[N],nxt[N<<1],to[N<<1],val[N<<1];

int tot,TOT;

int in[N],out[N],id[N];

LL has[N],dep[N];

int root[N],lc[N*20],rc[N*20],cnt[N*20];
LL sum[N*20];

LL ans; int num;

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

void add(int u,int v,int w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w;
}

void predfs(int x,int y)
{
    in[x]=++tot; id[tot]=x;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=y) dep[to[i]]=has[to[i]]=dep[x]+val[i],predfs(to[i],x);
    out[x]=tot;
}

void insert(int &x,int y,int l,int r,int pos)
{
    x=++tot;
    sum[x]=sum[y]+has[pos]; cnt[x]=cnt[y]+1;
    if(l==r) return; 
    int mid=l+r>>1;
    if(pos<=mid) rc[x]=rc[y],insert(lc[x],lc[y],l,mid,pos);
    else lc[x]=lc[y],insert(rc[x],rc[y],mid+1,r,pos);
}

void build()
{
    sort(has+1,has+n+1);
    TOT=unique(has+1,has+n+1)-has-1;
    tot=0;
    for(int i=1;i<=n;++i) 
    insert(root[i],root[i-1],1,TOT,lower_bound(has+1,has+TOT+1,dep[id[i]])-has);
}

void query(int x,int y,int l,int r,int pos)
{
    if(l==r)
    {
        ans+=sum[x]-sum[y];
        num+=cnt[x]-cnt[y];
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid)
    {
        ans+=sum[rc[x]]-sum[rc[y]];
        num+=cnt[rc[x]]-cnt[rc[y]];
        query(lc[x],lc[y],l,mid,pos);
    }
    else query(rc[x],rc[y],mid+1,r,pos);
}

int main()
{
    freopen("data.in","r",stdin);
     freopen("my.out","w",stdout);
    int x,y;
    read(n);
    for(int i=1;i<n;++i) 
    {
        read(x); read(y);
        add(i+1,x,y);
    }
    tot=0; 
    predfs(1,0);
    build();
    int m,pos;
    read(m);
    while(m--)
    {
        read(x); read(y);
        pos=lower_bound(has+1,has+TOT+1,dep[x]+y)-has;
        if(pos>TOT) puts("0");
        else
        {
            ans=0; num=0;
            query(root[out[x]],root[in[x]-1],1,TOT,pos);
            cout<<ans-num*dep[x]<<'
';
        }
    }
}
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

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
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7894393.html