NOIP2016 天天爱跑步

3 篇文章 0 订阅
3 篇文章 0 订阅

对于每条链,可以拆成两条,一条S到Lca,一条Lca到T,可以发现前者能被一个在now位置的观察者观察到的条件是0+dep[S]=w[now]+dep[now],因为向上跑时深度和时间的和不变,所以可以以深度和时间的和未关键字做hash,因为这条链不到根,所以用经典手法将这条链拆成两条到根的链。另一条Lca到T被在now位置的观察者观察到的条件是time[x]-dep[x]=w[now]-dep[now]。因为向下走的时候时间和深度的差不变,所以可以作为关键字做hash。
一条链拆成若干条链后统计答案要统计当前点子树的贡献,考虑到一棵树的子树的dfs序一定是一个连续的区间,所以可以在进这棵子树时减,出这棵子树时加,得到的就是这棵子树的贡献了。

type
  rec1=record
         y,next:longint;
       end;
  rec2=record
         v,k,next:longint;
       end;
const
  MAXN=300000;
var
  map:array[0..MAXN*2] of rec1;
  rec:array[0..1,0..MAXN*2] of rec2;
  last:array[0..1,0..MAXN] of longint;
  hash:array[0..1,-MAXN..MAXN] of longint;
  fa:array[0..MAXN,0..20] of longint;
  first,dep,w,ans:array[0..MAXN] of longint;
  s:array[0..1] of longint;
  n,m,i,u,v,a,b,l,ss:longint;
procedure swap(var a,b:longint);
  var t:longint;
  begin t:=a;a:=b;b:=t; end;
procedure ins(x,y:longint);
  begin
    inc(ss);map[ss].y:=y;
    map[ss].next:=first[x];first[x]:=ss;
  end;
procedure dfs1(x:longint);
  var
    i,t,y:longint;
  begin
    for i:=1 to 20 do
      fa[x][i]:=fa[fa[x][i-1]][i-1];
    t:=first[x];
    while (t>0) do
      begin
        y:=map[t].y;
        if (y<>fa[x][0]) then
          begin
            fa[y][0]:=x;
            dep[y]:=dep[x]+1;
            dfs1(y);
          end;
        t:=map[t].next;
      end;
  end;
function lca(a,b:longint):longint;
  var
    i:longint;
  begin
    if (dep[a]<dep[b])
      then swap(a,b);
    for i:=20 downto 0 do
      if (dep[a]-1<<i>=dep[b])
        then a:=fa[a][i];
    for i:=20 downto 0 do
      if (fa[a][i]<>fa[b][i]) then
        begin
          a:=fa[a][i];
          b:=fa[b][i];
        end;
    if (a=b)
      then exit(a)
      else exit(fa[a][0]);
  end;
procedure add(kd,x,v,k:longint);
  begin
    inc(s[kd]);rec[kd][s[kd]].v:=v;rec[kd][s[kd]].k:=k;
    rec[kd][s[kd]].next:=last[kd][x];last[kd][x]:=s[kd];
  end;
procedure dfs2(x:longint);
  var
    t,y:longint;
  begin
    dec(ans[x],hash[0][w[x]+dep[x]]);
    dec(ans[x],hash[1][w[x]-dep[x]]);
    t:=first[x];
    while (t>0) do
      begin
        y:=map[t].y;
        if (y<>fa[x][0])
          then dfs2(y);
        t:=map[t].next;
      end;
    t:=last[0][x];
    while (t>0) do
      begin
        inc(hash[0][rec[0][t].v],rec[0][t].k);
        t:=rec[0][t].next;
      end;
    t:=last[1][x];
    while (t>0) do
      begin
        inc(hash[1][rec[1][t].v],rec[1][t].k);
        t:=rec[1][t].next;
      end;
    inc(ans[x],hash[0][w[x]+dep[x]]);
    inc(ans[x],hash[1][w[x]-dep[x]]);
  end;
begin
  read(n,m);
  for i:=1 to n-1 do
    begin
      read(u,v);
      ins(u,v);
      ins(v,u);
    end;
  dep[1]:=1;fa[1][0]:=0;dfs1(1);
  for i:=1 to n do read(w[i]);
  for i:=1 to m do
    begin
      read(a,b);
      l:=lca(a,b);
      add(0,a,dep[a],1);
      add(0,fa[l][0],dep[a],-1);
      add(1,b,dep[a]-2*dep[l],1);
      add(1,l,dep[a]-2*dep[l],-1);
    end;
  dfs2(1);
  for i:=1 to n-1 do
    write(ans[i],' ');
  writeln(ans[n]);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值