jzoj. 3519. 【NOIP2013模拟11.6A组】灵能矩阵(pylon)

26 篇文章 0 订阅

Description

Protoss 的灵能矩阵由若干个节点所构成。它们构成了一棵有根树,树根为1 号节点。定义没有子节点的节点为叶节点。叶节点内储存着一定量的能量,而非叶节点的能量为它子树中所有叶节点的能量之和。

如果一个节点的每一个子节点的能量都相同,那么这个节点就是能量平衡的。如果矩阵内每一个节点都能量平衡,则这个矩阵是能量平衡的。

被你所接管的这个灵能矩阵,似乎在长期的废弃之后已经无法保持的能量的平衡。为了重新让矩阵平衡,你可以通过将叶节点储存的能量散逸到太空中。你不可以使一个叶节点储存的能量为负数。

你希望求出最少散逸多少能量到太空中就能使灵能矩阵的能量平衡。

Input

第一行包含一个整数n,表示节点的数量。

接下来一行,包含A1,A2…An 这n 个非负整数,表示每个节点自身储存的能量。保证储存能量的节点都是叶节点。

接下来n -1 行,每行包含两个数字Si, Ti,描述一条从Si 号节点到Ti 号节点的边。

Output

第一行包含一个整数,表示最少要散逸多少单位的能量才能使灵能矩阵的能量平衡。

Sample Input

6

0 0 12 13 5 6

1 2

1 3

1 4

2 5

2 6

Sample Output

6

Data Constraint

对于15% 的数据,1<= n <= 10。

对于30% 的数据,对于边Si, Ti,Si + 1 ̸= Ti 的边数不超过3 条。

对于50% 的数据,1<= n <= 1000。

对于100% 的数据,1 <= n <= 100000,1 <= Ai <= 2^30。

分析:先说一句,每个叶子节点最后的值一定是整数。我们考虑每个叶子节点都是1,往上跑,如果遇到有儿子不相等的情况,显然要把他们变为最小公倍数。

         / \
       /    \
     2       3
    / \    / | \
   1   1  1  1  1

最上面是什么呢??我们可以把下面的2,3的子树全部乘3和2,使他们变为最小公倍数,这时

         12
        / \
       /    \
     6       6
    / \    / | \
   3   3  2  2  2

这时,左右相等了,最上面的根显然是12。所以对于一个节点i,他的值为
f[i]=lcm(f[i.son1],f[i.son2]……f[i.sonx])*x {x为i儿子数,此时的儿子是没有变为公倍数的,就是2,3那个。}。这时,下面的儿子显然也没有变。所以树应该是这样的,满足上面的式子:

         12
        / \
       /    \
     2       3
    / \    / | \
   1   1  1  1  1

我们可以用上面的方法自下而上求出f[i],再自上而下更新f[i],使树变为状态2,就是第二个图,因为每次把子树都进行乘法,显然会超时。
这时
f[i.son]=f[i]/f[i].x {f[i].x为i的儿子数}

这时若最上面的数为12x,则叶子节点值就为{3x,3x,2x,2x,2x},如果叶子节点集合为{a1,a2,a3,a4,a5},那么我们可以结合不等式求出x的最小值,因为节点值只能变小,有
3x<=a1
3x<=a2
2x<=a3
2x<=a4
2x<=a5

所以,x<=min(ai/f[i]),因为是整数,要向下取整。对于最小的x,乘于系数(就是f[i]),就是这个点要变成的值,用原来的值减去它,再求和即可。

代码:

type
 node=record
  y,next:int64;
 end;

var
 a,ls:array [1..100001] of int64;
 f,e:array [1..100001] of int64;
 v:array [1..100001] of boolean;
 g:array [1..200001] of node;
 n,m,ans,x,y,d,min:int64;
 i,j:longint;

function lcm(x,y:int64):int64;
 var r,s,t:int64;
begin
 s:=x; t:=y;
 r:=x mod y;
 while r>0 do
  begin
   x:=y;
   y:=r;
   r:=x mod y;
  end;
 exit(s div y*t);
end;

procedure dfs(x:int64);
 var t:int64;
begin
 f[x]:=1;
 if a[x]<>0 then exit;
 t:=ls[x];
 while t>0 do
  begin
   with g[t] do
    begin
     if v[y]=false then
      begin
       v[y]:=true;
       dfs(y);
       inc(e[x]);
       f[x]:=lcm(f[x],f[y]);
      end;
     t:=next;
    end;
  end;
 f[x]:=f[x]*e[x];
end;

procedure dfsq(x:longint);
 var t:int64;
begin
 if a[x]<>0 then exit;
 t:=ls[x];
 while t>0 do
  begin
   with g[t] do
    begin
     if v[y]=false then
      begin
       v[y]:=true;
       f[y]:=f[x] div e[x];
       dfsq(y);
      end;
     t:=next;
    end;
  end;
end;

begin
 assign(input,'pylon.in');
 assign(output,'pylon.out');
 reset(input);
 rewrite(output);
 readln(n);
 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);
 fillchar(v,sizeof(v),false);
 v[1]:=true;
 dfsq(1);
 min:=maxlongint;
 for i:=1 to n do
  if (a[i] div f[i]<min) and (a[i]<>0) then
   min:=a[i] div f[i];
 for i:=1 to n do
  if a[i]<>0 then
   ans:=ans+a[i]-min*f[i];
 writeln(ans);
 close(input);
 close(output);
end.


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值