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
所以直接将堆顶踢出堆 即合并其左儿子与右儿子
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }