思路
这题也有很多方法,其实并不用可并堆。类似于上一种方法,计算出dfs序和每个点的深度,然后按深度降序排序。对于每个点,需要删除深度相差超过L的点,并加入当前点,在子树中统计答案。而这些用树状数组就可以了(单点修改+区间查询)。时间复杂度$O(N\log N)$
当然,还有一种更加巧妙的方法。用倍增求出$2^i$步祖先,然后就可以找到第一个距离超过L的祖先,用差分区间加,最后就能算出答案。时间复杂度也是$O(N\log N)$,下面提供了官方题解中的这种方法的代码供参考。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,id[N],r[N],now,ans[N];
pair<long long,int> a[N];
struct BIT
{
int tree[N];
void modify(int x,int val)
{
for(;x<=n;x+=x&-x)
tree[x]+=val;
}
int query(int x)
{
int ans=0;
for(;x;x-=x&-x)
ans+=tree[x];
return ans;
}
}T;
//树状数组模板
vector<pair<int,long long>> mat[N];
void dfs(int k)
{
id[k]=++now;
//dfs序
for(auto e:mat[k])
{
a[e.first]=make_pair(a[k].first+e.second,e.first);
dfs(e.first);
}
r[k]=now;
//结束时间戳
}
int main()
{
long long l;
cin>>n>>l;
for(int i=2;i<=n;i++)
{
int p;
long long w;
cin>>p>>w;
mat[p].push_back(make_pair(i,w));
}
a[1]=make_pair(0ll,1);
dfs(1);
sort(a+1,a+n+1);
int j=n;
for(int i=n;i;i--)
{
for(;a[j].first-a[i].first>l;j--)
T.modify(id[a[j].second],-1);
//删除超过距离超过L的点
T.modify(id[a[i].second],1);
//插入当前点
ans[a[i].second]=T.query(r[a[i].second])-T.query(id[a[i].second]-1);
//统计子树答案
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<endl;
return 0;
}
官方题解中的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var ans,d:array[0..200333]of int64;
i,n,j,v:longint;
len:int64;
p:array[0..200333,0..19]of longint;
begin
assign(input,'runaway.in');reset(input);
assign(output,'runaway.out');rewrite(output);
read(n,len);
ans[1]:=1;
for i:=2 to n do
begin
read(p[i,0]);
read(d[i]);
d[i]:=d[i]+d[p[i,0]];
//计算深度
for j:=1 to 18 do
p[i,j]:=p[p[i,j-1],j-1];
//倍增求祖先
v:=i;
for j:=18 downto 0 do
if d[i]-d[p[v,j]]<=len then v:=p[v,j];
//找到最远的距离不超过L的祖先
inc(ans[i]);
dec(ans[p[v,0]]);
//差分
end;
for i:=n downto 1 do
ans[p[i,0]]:=ans[p[i,0]]+ans[i];
for i:=1 to n do
Writeln(ans[i]);
end.