bzoj3011 [Usaco2012 Dec]Running Away From the Barn (可并堆)

Description

It’s milking time at Farmer John’s farm, but the cows have all run away! Farmer John needs to round them all up, and needs your help in the search. FJ’s farm is a series of N (1 <= N <= 200,000) pastures numbered 1…N connected by N - 1 bidirectional paths. The barn is located at pasture 1, and it is possible to reach any pasture from the barn. FJ’s cows were in their pastures this morning, but who knows where they ran to by now. FJ does know that the cows only run away from the barn, and they are too lazy to run a distance of more than L. For every pasture, FJ wants to know how many different pastures cows starting in that pasture could have ended up in. Note: 64-bit integers (int64 in Pascal, long long in C/C++ and long in Java) are needed to store the distance values.

给出以1号点为根的一棵有根树,问每个点的子树中与它距离小于l的点有多少个。

Input

* Line 1: 2 integers, N and L (1 <= N <= 200,000, 1 <= L <= 10^18)

* Lines 2..N: The ith line contains two integers p_i and l_i. p_i (1 <= p_i < i) is the first pasture on the shortest path between pasture i and the barn, and l_i (1 <= l_i <= 10^12) is the length of that path.

Output

* Lines 1..N: One number per line, the number on line i is the number pastures that can be reached from pasture i by taking roads that lead strictly farther away from the barn (pasture 1) whose total length does not exceed L.

Sample Input

4 5
1 4
2 3
1 5

Sample Output

3
2
1
1
OUTPUT DETAILS: Cows from pasture 1 can hide at pastures 1, 2, and 4. Cows from pasture 2 can hide at pastures 2 and 3. Pasture 3 and 4 are as far from the barn as possible, and the cows can hide there.

HINT

Source

[ Submit][ Status][ Discuss]


分析:
果然可并堆的题目隐藏的都很深,不是每道题都像罗马游戏那样让你练板子的

受到这道题的启发
这道题乍看上去和可并堆没有什么关系,

题目有一个很奇特的限制:计算子树内到达根结点距离不超过L的结点个数

因为是路劲一端一定是根结点,所以我们可以simple的用两个结点深度相减得到距离

同时,这提示我们可以dfs维护子树信息
假设我们已经知道x这棵子树中满足条件的结点数,
向上回溯的时候,我们需要把儿子的信息都合并到父亲上
但是这样就会出现一些不符合条件的结点
我们就需要查找出这些结点并且删除ta们
这些要被删除的结点有一个显而易见的性质:ta们的深度一定很大
所以我们可以把结点按照dis从大到小判断

这样来看,我们需要一种数据结构,支持:快速查找最大值,删除,合并

可并堆

tip

题目中描述有误,应为“小于等于l”

我发现网上的代码中
对于根结点的查找,有的用的是并查集,有的直接用一个数组
我仔细的观察了一下:
如果是题目给出的是集合的形式,那么多半使用并查集查询根结点
如果题目本身就是一种树形结构,这样在dfs并且合并的时候,在一定程度上是有序的,所以可以用一个数组完成根结点的维护

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N=200010;
int n,ch[N][2],root[N],dis[N],sz[N];
ll val[N],m; 
struct node{
    int x,y,nxt;
    ll v;
};
node way[N<<1];
int st[N],tot=0;

void add(int u,int w,ll z)
{
    tot++;
    way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
}

int merge(int x,int y)
{
    if (!x) return y;
    if (!y) return x;
    if (val[x]<val[y]) swap(x,y);
    ch[x][1]=merge(ch[x][1],y);
    if (dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]);
    if (!ch[x][1]) dis[x]=0;
    else dis[x]=dis[ch[x][1]]+1;
    return x;
}

void dfs(int now)
{   
    sz[now]=1,root[now]=now;
    for (int i=st[now];i;i=way[i].nxt)
    {
        val[way[i].y]=val[now]+way[i].v;    //深度 
        dfs(way[i].y);
        sz[now]+=sz[way[i].y];

        root[now]=root[way[i].y]=merge(root[now],root[way[i].y]);
    }

    while (val[root[now]]-val[now]>m)        
    {
        sz[now]--;
        root[now]=merge(ch[root[now]][0],ch[root[now]][1]);
    }
}

int main()
{
    scanf("%d%lld",&n,&m);
    for (int i=2;i<=n;i++)
    {
        int x;
        ll z;
        scanf("%d%lld",&x,&z);
        add(x,i,z);
    }

    dfs(1);

    for (int i=1;i<=n;i++)
        printf("%d\n",sz[i]);
    return 0;
}
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页