USACO Running Away From the Barn /// 可并堆 左偏树维护大顶堆

题目大意:

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

 

左偏树 https://blog.csdn.net/pengwill97/article/details/82874235

题解 https://www.cnblogs.com/GXZlegend/p/6532881.html

若y在x的子树内 那么x到y的距离 等于 dis(1,y)-dis(1,x)

所以DFS时处理出节点到根(点1)的距离

然后自底向上合并 维护距离大顶堆 

那么当 堆顶到根的距离 > m+当前点到根的距离 时 说明距离大于m

同时也说明再继续向上回溯合并时 这个堆顶对应的点与那些点的距离也肯定会超过m

所以直接将堆顶踢出堆 即合并其左儿子与右儿子 

 

#include <bits/stdc++.h>
#define INF 0x3f3f3f
#define LL long long
#define mem(i,j) memset(i,j,sizeof(i))
#define inc(i,j,k) for(int i=j;i<=k;i++)
#define dec(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
const int N=2e5+5;
const int mod=1e9+7;

int n;
LL L, v[N];
int dis[N], ans[N];
int l[N], r[N], root[N];
struct NODE { int to; LL len; };
vector <NODE> G[N];
void init() {
    mem(dis,0); mem(v,0);
    inc(i,1,n) G[i].clear();
}
void addE(int u,int v,LL w) {
    G[u].push_back({v,w});
}
int unite(int x,int y) {
    if(!x) return y;
    if(!y) return x;
    if(v[x]<v[y]) swap(x,y);
    r[x]=unite(r[x],y);
    if(dis[l[x]]<dis[r[x]]) swap(l[x],r[x]);
    dis[x]=dis[r[x]]+1;
    return x;
}
void DFS(int u) {
    root[u]=u; ans[u]=1;
    int len=G[u].size()-1;
    inc(i,0,len) {
        NODE e=G[u][i];
        v[e.to]=v[u]+e.len;
        DFS(e.to);
        ans[u]+=ans[e.to];
        root[u]=unite(root[u],root[e.to]);
    }
    while(v[root[u]]>L+v[u]) {
        ans[u]--;
        root[u]=unite(l[root[u]],r[root[u]]);
    }
}

int main()
{
    while(~scanf("%d%lld",&n,&L)) {
        init();
        inc(i,2,n) {
            int v; LL w;
            scanf("%d%lld",&v,&w);
            addE(v,i,w);
        }
        dis[0]=-1; DFS(1);
        inc(i,1,n) printf("%d\n",ans[i]);
    }

    return 0;
}
View Code

 

posted @ 2019-03-08 22:19  _Jessie  阅读(165)  评论(0编辑  收藏  举报