Description
小C得到了一棵树,这棵树每个点都有一个权值且1为根节点。无聊的小C又随机了一个权值s,现在他想知道这棵树上有多少条路径的节点权值总和恰好为s,且满足该路径中节点的深度必须是升序的。
Input
第一行是两个正整数n,s,接下来一行n个正整数,表示每个节点的权值。
接下来n-1行,每行包含两个正整数,表示树上的一条边。
Output
输出一个数,表示满足条件的路径数。
Sample Input
3 3
1 2 3
1 2
1 3
Sample Output
2
Hint
对于30%的数据,n<=1000.
对于100%的数据,n<=100000,s,节点权值<=1000.
分析:一眼看去就是之前的树上倍增。但很多人暴力就过了,很不服。
暴力:枚举每个点,再往上跑,用sum统计路径上的和。当sum>=s就可以break掉。
倍增:预处理出f[i,j]为从i开始,走2^j步,不包括i的和。跑的时候,先判断权值是否已经大于s,在开始倍增跑出等于s的数,单调性显然。
代码:
const
maxn=100001;
type
node=record
y,next:longint;
end;
var
g:array [0..maxn*2] of node;
ls,a:array [0..maxn] of longint;
v:array [0..maxn] of boolean;
f,sum:array [0..maxn,0..20] of longint;
n,s,i,j,x,y,num,ans:longint;
procedure dfs(x:longint;fa:longint);
var t:int64;
begin
t:=ls[x];
f[x,0]:=fa;
sum[x,0]:=a[fa];
while t>0 do
begin
with g[t] do
begin
if v[y]=false then
begin
v[y]:=true;
dfs(y,x);
end;
t:=next;
end;
end;
end;
begin
readln(n,s);
for i:=1 to n do
read(a[i]);
for i:=1 to n-1 do
begin
readln(x,y);
g[i].y:=y;
g[i].next:=ls[x];
ls[x]:=i;
g[i+n].y:=x;
g[i+n].next:=ls[y];
ls[y]:=i+n;
end;
v[1]:=true;
dfs(1,0);
for j:=1 to 20 do
for i:=1 to n do
begin
f[i,j]:=f[f[i,j-1],j-1];
sum[i,j]:=sum[i,j-1]+sum[f[i,j-1],j-1];
end;
for i:=1 to n do
begin
num:=a[i];
j:=i;
if num>s then continue;
x:=18;
while x>=0 do
begin
if f[j,x]>0 then
if num+sum[j,x]<=s then
begin num:=num+sum[j,x]; j:=f[j,x]; end;
x:=x-1;
end;
if num=s then inc(ans);
end;
writeln(ans);
end.