题目链接:传送门
题目大意:
给出以 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
#include
#include
#include
#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;
}