luoguP3066 [USACO12DEC]逃跑的BarnRunning
luoguP3066 [USACO12DEC]逃跑的BarnRunning
题目大意
给定一棵n个节点的树和参数L,查询每个节点子树中到达该节点距离<=L的数量(包括该节点)
偏模板的主席树
PS:注意一下输入格式
dfs:得出每个子树时间戳区间,每个节点到根节点的距离,仔细理解一下几个数组的含义吧,dalao直接略过
update:这题以时间戳(i)为关键字继承(i-1)
query:直接查询每个子树的时间戳区间里<=dep[i]+L
这里用到了一个小技巧,就是为了避免特判,b数组加一个虚节点inf,查询<now的就好了
My complete code:
#include<cstdio> #include<algorithm> using namespace std; typedef long long LL; const LL maxn=3e5; const LL inf=1e18; inline LL read(){ LL x=0,f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); }return x*f; } struct node{ LL to,next,d; }dis[maxn]; LL n,L,num,nod,num1,cnt; LL head[maxn],dep[maxn],b[maxn],low[maxn],dfn[maxn],sot[maxn],date[maxn<<7],lt[maxn<<7],rt[maxn<<7],root[maxn]; inline void add(LL u,LL v,LL d){ dis[++num]=(node){v,head[u],d}; head[u]=num; } void dfs(LL u,LL fa){ sot[++num1]=u; b[++cnt]=dep[u]; dfn[u]=++num; for(LL i=head[u];i;i=dis[i].next){ LL v=dis[i].to; dep[v]=dep[u]+dis[i].d; dfs(v,u); } low[u]=num; } void update(LL &now,LL pre,LL l,LL r,LL c){ now=++nod; date[now]=date[pre]+1; if(l==r) return; LL mid=(l+r)>>1; if(c<=mid){ update(lt[now],lt[pre],l,mid,c); rt[now]=rt[pre]; }else{ update(rt[now],rt[pre],mid+1,r,c); lt[now]=lt[pre]; } } LL query(LL pre,LL next,LL l,LL r,LL c){//<c LL mid=(l+r)>>1; if(r<c) return date[next]-date[pre]; LL ans=0; if(l<c) ans+=query(lt[pre],lt[next],l,mid,c); if(mid+1<c) ans+=query(rt[pre],rt[next],mid+1,r,c); return ans; } int main(){ scanf("%lld%lld",&n,&L); for(LL v=2;v<=n;++v){ LL u=read(),e=read(); add(u,v,e); } num=0; dfs(1,0); sort(b+1,b+1+cnt); b[++cnt]=inf; cnt=unique(b+1,b+1+cnt)-b-1; for(LL i=1;i<=n;++i){ LL u=sot[i]; LL now=lower_bound(b+1,b+1+cnt,dep[u])-b; update(root[i],root[i-1],1,cnt,now); } for(LL i=1;i<=n;++i){ LL l=dfn[i],r=low[i]; LL now=upper_bound(b+1,b+1+cnt,dep[i]+L)-b; printf("%lld\n",query(root[l-1],root[r],1,cnt,now)); } return 0; }/* 4 5 1 4 2 3 1 5 3 2 1 1 */