【NOIP2018复习】树(期望/树形DP/LCA)

时间限制:2000MS内存限制:256000KB

题目描述

梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地 选择一条走过去, 为了确保第二天能够准时到校, 你要求出每个梦期望经过多少条边才能苏 醒. 为了避免精度误差, 你要输出答案模10^9 + 7的结果.


HIT

输入

第一行两个整数分别代表 N 和 Q. 接下来 N-1 行, 每行两个整数 u, v 代表树中的一条边. 接下来 Q 行, 每行两个整数代表询问的 u,v. 

输出

一共 Q 行, 每行一个整数代表答案

输入样例复制

4 2
1 2
2 3
3 4
1 4
3 4

输出样例复制

9

说明

Data Constraint 对于 20%的数据, N <= 10. 对于 40%的数据, N <= 1000. 另有 20%的数据, 保证给定的树是一条链. 对于 100%的数据, N <= 100000, Q <= 100000. 

 

题解:设f[i]为从i结点走到i的父亲的期望长度,g[i]为i结点的父亲走到i的期望长度

            对于f[i]有两种情况,一、先走到儿子再走到自己再走到父亲

                                                二、直接走到父亲

     这里写图片描述

这里写图片描述

化简可得这里写图片描述

化简:两边同乘deg,右边的f[u]/g[u]移到左边发现系数剩下1

求出f[i],g[i]后,因为期望具有线性,所以做树上前缀和,fs[i]表示i到树根的期望值和,gs[i]同理

求出lca后ans=fs[i]-fs[lca]+gs[i]-gs[lca]

const
  maxn=1000000;
  p=1000000007;

var
  n,q,kk,ans,x,y,ffa,i:longint;
  e,last,next:array[0..maxn*2]of longint;
  fa,f,g,sum:array[1..maxn]of int64;
  ff:array[0..maxn,0..31]of int64;
  v:array[1..maxn]of boolean;
  du,dep,fs,gs:array[0..maxn]of int64;
  lg:array[1..maxn]of int64;
  mi:array[0..30]of int64;

procedure add(x,y:int64);
begin
  inc(e[0]);
  e[e[0]]:=y;
  next[e[0]]:=last[x];
  last[x]:=e[0];
end;

procedure build(x,y:longint);
var
  i:longint;
begin
  i:=last[x];
  dep[x]:=y; if y>kk then kk:=y;
  while i<>0 do
  begin
    inc(du[x]);
    if not v[e[i]] then
    begin
      v[e[i]]:=true;
      fa[e[i]]:=x;
      build(e[i],y+1);
    end;
    i:=next[i];
  end;
end;

procedure dfs1(x:longint);
var
  i,go:longint;
begin
  if (fa[x]<>0)and(du[x]=1) then
  begin
    f[x]:=1;
    exit;
  end;
  v[x]:=true;
  i:=last[x];
  while i<>0 do
  begin
    go:=e[i];
    if v[go] then
    begin
      i:=next[i];
      continue;
    end;
    dfs1(e[i]);
    sum[x]:=sum[x]+f[e[i]];
    f[x]:=sum[x]+du[x];

    i:=next[i];
  end;
end;

procedure dfs2(x:longint);
var
  i,go:longint;
begin
  i:=last[x];v[x]:=true;
  fs[x]:=fs[fa[x]]+f[x];
  gs[x]:=gs[fa[x]]+g[x];
  while i<>0 do
  begin
    go:=e[i];
    if v[e[i]] then
    begin
      i:=next[i];
      continue;
    end;
    g[go]:=g[x]+du[x]+sum[x]-f[go];
    dfs2(e[i]);
    i:=next[i];
  end;

end;


procedure init;
var
  i,j:longint;
begin
  readln(n,q);
  for i:=1 to n-1 do
  begin
    readln(x,y);
    add(x,y);
    add(y,x);
  end;
  v[1]:=true;
  build(1,1);

  fs[1]:=f[1];gs[1]:=g[1];
  fillchar(v,sizeof(v),false);
  dfs1(1);
  fillchar(v,sizeof(v),false);
  dfs2(1);
  mi[0]:=1;
  for i:=1 to 30 do
    mi[i]:=mi[i-1]*2;
  for i:=1 to maxn do
    lg[i]:=trunc(ln(i)/ln(2));
  for i:=1 to n do
    ff[i,0]:=fa[i];
  for j:=1 to lg[kk] do
    for i:=1 to n do
      ff[i,j]:=ff[ff[i,j-1],j-1];
end;

function lca(x,y:longint):longint;
var
  t,i,j:longint;
begin
  if dep[x]<dep[y] then
  begin
    t:=x;x:=y;y:=t;
  end;

  while dep[x]>dep[y] do
    x:=ff[x,lg[dep[x]-dep[y]]];
  if x=y then exit(x);
  for i:=lg[dep[x]] downto 0 do
    if ff[x,i]<>ff[y,i] then
    begin
      x:=ff[x,i];
      y:=ff[y,i];
    end;
  lca:=fa[x];
end;

begin
  init;
  for i:=1 to q do
  begin
    readln(x,y);
    ffa:=lca(x,y);
    ans:=(fs[x]-fs[ffa]+gs[y]-gs[ffa])mod p;
    writeln(ans);
  end;
end.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值