题目描述
又到了FJ农场的挤奶时间了,但是奶牛都跑了!FJ需要把它们全部抓回来,并且需要你的帮助。
FJ的农场是由N(1≤N≤200000)个牧场组成,编号为1到N,由N-1条无向边连通。谷仓位于牧场1,并且从谷仓出发可以到达任何一个牧场。
FJ的奶牛今天早上都在它们的牧场,但没有人知道它们现在跑到哪里了。它们只能往远离谷仓的方向跑,由于它们太懒了,它们最多只能跑不超过L的距离。FJ想知道对于每一个牧场的奶牛能跑到的牧场的个数。
简述题意:给出以1号点为根的一棵有根树,问每个点的子树中与它距离小于等于l的点有多少个。
输入格式
第一行两个整数N和L (1 <= N <= 200,000, 1 <= L <= 10^18)
第2到N行,第i行有两个整数pi和Li, pi (1 <= pi < i)是i到谷仓路径上的第一个牧场,Li(1<=Li<=10^12)是这两个牧场之间的长度。
输出格式
输出共N行,每行一个整数,第i行表示第i个牧场能够到达的牧场的个数。
数据范围
100%的数据1 <= N <= 200,000, 1 <= L <= 10^18
分析
首先可以用暴力的方法,对每个点跑一遍Dfs,可做,但复杂度最坏可到O(n2)O(n^2)O(n2)。想想其他算法?发现可以对每个点开一个左偏树,维护它的子树到这个该点的父亲节点的距离值,大根堆。在Dfs过程中,对于子树,先判断子树的堆里面的最大值是否大于L,若大于则出堆,直到小于等于;而后将该子树与该节点合并,并累加答案。递归完子树后,将该节点的左偏树的值全都加上该点到其父亲节点的距离,可以打标记,合并时下传标记。
这样有点麻烦,可以改为维护到根节点的距离值,子树判断时,条件改为堆顶的值-该节点的值是否大于L。这样就不需要打标记了。
代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int N=200005;
typedef long long LL;
int nxt[N<<1],to[N<<1];
int h[N],cnt,n,ans[N],sz[N];
LL l,wei[N<<1],v[N],add[N];
int L[N],R[N],f[N],d[N];
void Add_Edge(int x,int y,LL z) {cnt++,nxt[cnt]=h[x],to[cnt]=y,wei[cnt]=z,h[x]=cnt;}
int Getf(int x) {return f[x]==x?x:f[x]=Getf(f[x]);}//找父亲
void Pushdown(int x) {v[L[x]]+=add[x],v[R[x]]+=add[x],add[L[x]]+=add[x],add[R[x]]+=add[x],add[x]=0;}
//标记下传
int Merge(int x,int y) {//合并子树
if (!x||!y) return x+y;
if (v[x]<v[y]) swap(x,y);
Pushdown(x);
Pushdown(y);
R[x]=Merge(R[x],y);
if (d[L[x]]<d[R[x]]) swap(L[x],R[x]);
d[x]=d[R[x]]+1;
return x;
}
void Dfs(int x,int fa,LL wp) {
int now=x;
for (int i=h[x];i;i=nxt[i]) {
int y=to[i];
if (y==fa) continue;
Dfs(y,x,wei[i]);
y=Getf(y);
while (v[y]>l&&y) {//判断条件
LL p=add[y],t=sz[y];
f[y]=Merge(L[y],R[y]);
f[f[y]]=f[y];
y=f[y];
if (f[y]) {
v[y]+=p;
add[y]+=p;
sz[y]=t-1;
}
}
y=Getf(to[i]);
if (!y) continue;
int t=sz[now]+sz[y];//合并子树
f[now]=f[y]=Merge(now,y);
now=f[now];
sz[now]=t;
}
add[now]+=wp;//标记累加
v[now]+=wp;
ans[x]=sz[now];//更新答案
}
int main() {
scanf("%d%lld",&n,&l);
for (int i=2;i<=n;i++) {
int u;
LL w;
scanf("%d%lld",&u,&w);
Add_Edge(i,u,w);
Add_Edge(u,i,w);
}
d[0]=-1;
for (int i=1;i<=n;i++) {
f[i]=i;
sz[i]=ans[i]=1;
v[i]=d[i]=0;
}
Dfs(1,0,0);
for (int i=1;i<=n;i++) {
printf("%d\n",ans[i]);
}
return 0;
}
本博客介绍了一个基于树形结构的算法问题,旨在解决FJ农场中奶牛的追踪难题。通过构建有根树并运用左偏树与大根堆策略,算法能够高效计算出每个牧场内奶牛可能达到的所有位置。详细解析了算法的设计思路、数据结构的选择与实现代码。
547

被折叠的 条评论
为什么被折叠?



