题意:给出以1号点为根的一棵有根树,问每个点的子树中与它距离小于l的点有多少个。n<=2e5,l<=1e18
随手抽了一道,以为是个水题,刷水不成反被刷。
一开始觉得这题不难,然后样例老过不去,然后看zyf2000的代码。。还是错的,最后直接拿她的交了一下,结果是错的。。一怒之下删掉重写。。
开始想的是倍增什么的,但是好像不好动态维护,而且复杂度好大。。
然后这就只能可并堆咯,堆维护每个子树内距离不超过L的点,从下往上走,走完一个子树就合并一波,然后动态把距离超过L的删掉就可以了。
很久没打左偏树,好丑。。
需要注意的是d[i]是左偏树的dist,dis[i]则是i到根的距离,刚好与左偏树的优先级是相同的。
记录lazy[x]来传递权值。f[x]表示x所在的堆的top。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=4e5+5;
typedef long long ll;
int head[N],next[N],go[N],d[N];
int ans[N],n;
ll dis[N],lazy[N],val[N],L;
int ls[N],rs[N],f[N],size[N],tot;
inline void add(int x,int y,ll z)
{
go[++tot]=y;
val[tot]=z;
next[tot]=head[x];
head[x]=tot;
}
inline void solve(int x,ll v)
{
if (!x)return;
dis[x]+=v;
lazy[x]+=v;
}
inline void pushup(int x)
{
size[x]=size[ls[x]]+size[rs[x]]+1;
}
inline void pushdown(int x)
{
if (lazy[x])solve(ls[x],lazy[x]),solve(rs[x],lazy[x]),lazy[x]=0;
}
inline int merge(int x,int y)
{
if (!x)return y;
if (!y)return x;
if (dis[x]<dis[y])swap(x,y);
pushdown(x);
rs[x]=merge(rs[x],y);
if (d[ls[x]]<d[rs[x]])swap(ls[x],rs[x]);
d[x]=d[rs[x]]+1;
pushup(x);
return x;
}
inline void pop(int x)
{
pushdown(f[x]);
f[x]=merge(ls[f[x]],rs[f[x]]);
}
inline void dfs(int x,int fa)
{
f[x]=x,d[x]=size[x]=1;
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (v!=fa)
{
dfs(v,x);
solve(f[v],val[i]);
while (dis[f[v]]>L)pop(v);
f[x]=merge(f[x],f[v]);
}
}
ans[x]=size[f[x]];
}
int main()
{
scanf("%d%lld",&n,&L);
fo(i,2,n)
{
int x;
ll z;
scanf("%d%lld",&x,&z);
add(x,i,z);
add(i,x,z);
}
dfs(1,0);
fo(i,1,n)printf("%d\n",ans[i]);
}