[USACO12DEC]逃跑的BarnRunning Away From… - 題解
阿新 • • 發佈:2018-12-26
題意簡述:給一棵以\(1\)為根的樹,節點有\(n\)個,求每個點以它為根的子樹中與它距離小於等於\(l\)的點有多少個。
解法:主席樹。按樹的\(dfs\)序建立一個主席樹(離散化)記錄深度,在同一子樹中的點一定在連續一段,計算與它距離等於\(l\)的點(假想的點)的排名。
程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n,bg[200010],ed[200010],idx=0,len=0; ll m,val[200010],w[200010],p[400010]; int cnt=0,hed[200010],to[200010],nxt[200010]; int rt[200010],Sum[10000010],L[10000010],R[10000010],tot=0; inline void add(int x,int y,ll z) { to[++cnt]=y,val[cnt]=z,nxt[cnt]=hed[x],hed[x]=cnt; } void dfs(int u) { bg[u]=++idx; for(int i=hed[u];i;i=nxt[i]) w[to[i]]=w[u]+val[i],dfs(to[i]); ed[u]=idx; } void update(int pre,int &u,int l,int r,int x) { u=++tot; Sum[u]=Sum[pre]+1,L[u]=L[pre],R[u]=R[pre]; if(l>=r) return ; int mid=(l+r)>>1; x<=mid? update(L[pre],L[u],l,mid,x):update(R[pre],R[u],mid+1,r,x); } void dfs2(int u) { ++idx,update(rt[idx-1],rt[idx],1,len,w[u]); for(int i=hed[u];i;i=nxt[i]) dfs2(to[i]); } int query(int u,int v,int l,int r,int x) { if(l>=r) return Sum[v]-Sum[u]; int mid=(l+r)>>1; if(x<=mid) return query(L[u],L[v],l,mid,x); return query(R[u],R[v],mid+1,r,x)+Sum[L[v]]-Sum[L[u]]; } int main() { scanf("%d%lld",&n,&m); for(int i=2;i<=n;i++) { int x; ll y; scanf("%d%lld",&x,&y),add(x,i,y); } dfs(1); for(int i=1;i<=n;i++) p[++len]=w[i],p[++len]=w[i]+m; sort(p+1,p+len+1),len=unique(p+1,p+len+1)-p-1; for(int i=1;i<=n;i++) w[i]=lower_bound(p+1,p+len+1,w[i])-p; // for(int i=1;i<=len;i++) printf("%lld ",p[i]);printf("\n"); idx=0,dfs2(1); for(int i=1;i<=n;i++) { int t=lower_bound(p+1,p+len+1,m+p[w[i]])-p; // printf(">>> %lld %lld\n",p[w[i]],p[t]); printf("%d\n",query(rt[bg[i]-1],rt[ed[i]],1,len,t)); } return 0; }