题目链接:传送门
题目大意:
给出以
1
1
1号点为根的一棵有根树,问每个点的子树中与它距离小于等于
L
L
L的点有多少个。
在节点子树内,想到用dfs序。
询问距离
<
=
L
<=L
<=L的点,想到用权值线段树。
又因为这里询问的是区间内距离
<
=
L
<=L
<=L的点,因此要珂持久化。
QWQ我好蒻啊
考虑主席树的维护和查询:
令
d
i
s
[
i
]
dis[i]
dis[i]表示第
i
i
i个节点到根的距离。
第
i
i
i棵主席树维护的是
d
f
s
dfs
dfs序为
1
~
i
1~i
1~i的节点的
d
i
s
dis
dis信息。
令
d
f
n
[
x
]
dfn[x]
dfn[x]表示
d
f
s
dfs
dfs序中
x
x
x出现的位置。
令
o
u
t
[
x
]
out[x]
out[x]表示
x
x
x的子树中
d
f
s
dfs
dfs序的最大值(即
x
x
x子树中
d
f
s
dfs
dfs序最大的节点的
d
f
s
dfs
dfs序)。
那么
x
x
x的子树的区间就是
[
d
f
n
[
x
]
,
o
u
t
[
x
]
]
[dfn[x],out[x]]
[dfn[x],out[x]]。
以上套个
d
f
s
dfs
dfs序模板即可,不再多说。
建完主席树后,假设当前节点到根距离为
d
i
s
[
x
]
dis[x]
dis[x],
则现在需要询问一段区间内有多少个节点到根距离
<
=
d
i
s
[
x
]
+
L
<=dis[x]+L
<=dis[x]+L。
考虑主席树的性质,主席树维护了前缀信息。
因此答案就是
(
(
(前
o
u
t
[
x
]
out[x]
out[x]个节点有多少个到根距离
<
=
d
i
s
[
x
]
+
L
<=dis[x]+L
<=dis[x]+L
)
−
(
)-(
)−(前
d
f
n
[
x
]
−
1
dfn[x]-1
dfn[x]−1个节点有多少个到根距离
<
=
d
i
s
[
x
]
+
L
<=dis[x]+L
<=dis[x]+L
)
)
)。
另外这里距离很大,离散化一下就珂以了qwq。
丑陋的代码:
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<queue>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
ll read() {
rl x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=200005;
const int LOG=20;
ll n,L,maxn,a[Size],b[Size],T[Size];
int cnt,tot,tim,head[Size],dfn[Size],out[Size];
struct Edge {
int v,next;
ll t;
} w[Size];
void AddEdge(int u,int v,ll t) {
w[++cnt].v=v;
w[cnt].t=t;
w[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int x,ll d) {
//a[i]表示dfs序为i的节点到根距离
dfn[x]=++tim;
a[tim]=d;
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
dfs(nxt,d+w[i].t);
}
out[x]=tim;
}
int ls[Size*LOG],rs[Size*LOG],sum[Size*LOG];
int update(int pre,int l,int r,int v) {
int rt=++tot;
ls[rt]=ls[pre]; rs[rt]=rs[pre];
sum[rt]=sum[pre]+1;
if(l<r) {
int mid=(l+r)>>1;
if(v<=mid) {
ls[rt]=update(ls[pre],l,mid,v);
} else {
rs[rt]=update(rs[pre],mid+1,r,v);
}
}
return rt;
}
int Query(int u,int v,int l,int r,int rt) {
//主席树区间求和
if(u<=l && r<=v) {
return sum[rt];
}
int mid=(l+r)>>1,ans=0;
if(u<=mid) ans+=Query(u,v,l,mid,ls[rt]);
if(v>mid) ans+=Query(u,v,mid+1,r,rs[rt]);
return ans;
}
int main() {
n=read();
L=read();
for(re i=2; i<=n; i++) {
int p=read();
ll t=read();
AddEdge(p,i,t);
}
dfs(1,0);
//离散化
memcpy(b,a,sizeof(a));
sort(b+1,b+1+n);
maxn=unique(b+1,b+1+n)-(b+1);
for(re i=1; i<=n; i++) {
int k=lower_bound(b+1,b+1+maxn,a[i])-b;
//按照dfs序建出主席树
T[i]=update(T[i-1],1,maxn,k);
}
for(re i=1; i<=n; i++) {
ll maxd=a[dfn[i]]+L;
//查找一下a[dfn[i]]+L在去重后在离散化数组b中的位置
int alb=upper_bound(b+1,b+1+maxn,maxd)-b-1;
int ans1=Query(1,alb,1,maxn,T[out[i]]);
int ans2=Query(1,alb,1,maxn,T[dfn[i]-1]);
printf("%d\n",ans1-ans2);
}
return 0;
}