2017年8月9日提高组T3 难题

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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值