jzoj 3583. 【GDOI2014模拟】小A的树

26 篇文章 0 订阅

Description

小A有一棵N个点的树,每个点都有一个小于2^20的非负整数权值。现在小A从树中随机选择一个点x,再随机选择一个点y(x、y可以是同一个点),并对从x到y的路径上所有的点的权值分别做and、or、xor运算,最终会求得三个整数。小A想知道,他求出的三个数的期望值分别是多少。

Input

输入文件包含多组测试数据。

第一行,一个整数T,表示测试数据的组数。

接下来的T节,每节表示一组测试数据,格式如下:

第一行,一个整数N。

第二行,N个整数,其中第i个整数表示第i个点的权值。

接下来的N-1行,每行两个整数a、b,表示树中有一条连接a、b的边。

Output

T行,每行三个实数,结果保留3位小数,其中第i行的三个实数表示由第i组测试数据求得的三个期望值(按照and、or、xor的顺序)。

Sample Input

1

4

1 2 3 4

1 2

2 3

2 4

Sample Output

0.875 4.250 3.375

Data Constraint

对于20%的数据,1<=N<=1000;

对于另外20%的数据,N个点构成一条链;

对于全部的数据,1<=N<=100000,1<=T<=5。

分析:
对于每一种运算,可以把每一位独立出来,给每一位生成一棵树,每个点的权值就是0或1,如果x到y的路径的运算结果为1,则该路径在该位有贡献。那么这棵树的贡献就是,part_sum*(2^k)/(n*n),k就是第k位。我们可以用树形dp求。设f[x,0/1],g[x,0/1],h[x,0/1]表示and,or,xor运算,从x子树内任意一点到x的路径最后结果为0/1的路径数,显然经过x的路径数就是两个不同儿子的状态相乘(这个状态自己推,是真的烦)。

注意爆栈。

代码:

const
 maxn=200001;
 maxv=20;

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

var
 f,g,h:array [1..maxn,0..1] of int64;
 a:array [1..maxn,1..40] of integer;
 ls:array [1..maxn] of longint;
 sf,sg,sh:array [1..maxn,0..1] of int64;
 adge:array [1..maxn*2] of node;
 v:array [1..maxn] of longint;
 t,n,i,m:longint;
 ef,eg,eh,c:extended;
 sumf,sumg,sumh:extended;
procedure add(x,y:longint);
 begin
  inc(m);
  adge[m].y:=y;
  adge[m].next:=ls[x];
  ls[x]:=m;
 end;

procedure dfs(x:longint;dep:longint);
 var t,i:longint;
begin
 f[x,0]:=0; f[x,1]:=0;
 g[x,0]:=0; g[x,1]:=0;
 h[x,0]:=0; h[x,1]:=0;
 sf[x,0]:=0; sh[x,0]:=0; sg[x,0]:=0;
 sf[x,1]:=0; sh[x,1]:=0; sg[x,1]:=0;
 f[x,a[x,dep]]:=1; g[x,a[x,dep]]:=1; h[x,a[x,dep]]:=1;
 t:=ls[x];
 while t>0 do
  begin
   with adge[t] do
    begin
     if v[y]=dep-1 then
      begin
       v[y]:=v[y]+1;
       dfs(y,dep);
       if a[x,dep]=0 then
        begin
         f[x,0]:=f[x,0]+f[y,0];
         f[x,1]:=f[x,1]+f[y,1];
         g[x,0]:=g[x,0]+g[y,1]+g[y,0];
         h[x,0]:=h[x,0]+h[y,0];
         h[x,1]:=h[x,1]+h[y,1];
         ef:=ef+sf[x,0]*f[y,1]+sf[x,1]*f[y,0];
         eh:=eh+sh[x,1]*h[y,0]+sh[x,0]*h[y,1]+sh[x,1]*h[y,1];
        end
       else
        begin
         f[x,0]:=f[x,0]+f[y,1];
         f[x,1]:=f[x,1]+f[y,0];
         g[x,0]:=g[x,0]+g[y,0];
         g[x,1]:=g[x,1]+g[y,1];
         h[x,1]:=h[x,1]+h[y,0]+h[y,1];
         ef:=ef+sf[x,0]*f[y,0]+sf[x,1]*f[y,1];
         eg:=eg+sg[x,1]*g[y,1];
         eh:=eh+(sh[x,0]+sh[x,1])*(h[y,0]+h[y,1]);
        end;
       sf[x,0]:=sf[x,0]+f[y,0];
       sf[x,1]:=sf[x,1]+f[y,1];
       sg[x,0]:=sg[x,0]+g[y,0];
       sg[x,1]:=sg[x,1]+g[y,1];
       sh[x,0]:=sh[x,0]+h[y,0];
       sh[x,1]:=sh[x,1]+h[y,1];
      end;
     t:=next;
    end;
  end;
 if a[x,dep]=0 then
  begin
   ef:=ef+sf[x,1];
   eh:=eh+sh[x,1];
  end
  else
   begin
    ef:=ef+sf[x,0]+0.5;
    eg:=eg+sg[x,1]+0.5;
    eh:=eh+sh[x,0]+sh[x,1]+0.5;
   end;
end;

procedure main;
 var i,x,k,y,j:longint;
 pow:extended;
begin
 readln(n);
 m:=0;
 for i:=1 to n do
  begin
   read(x); k:=0;
   while x>0 do
    begin
     inc(k);
     a[i,k]:=x and 1;
     x:=x shr 1;
    end;
   for j:=k+1 to 20 do
    a[i,j]:=0;
   ls[i]:=0; v[i]:=0;
  end;
 for i:=1 to n-1 do
  begin
   readln(x,y);
   add(x,y);
   add(y,x);
  end;
 sumf:=0; sumg:=0; sumh:=0; pow:=1;
 for i:=1 to 20 do
  begin
   ef:=0; eh:=0; eg:=0;
   v[1]:=i;
   dfs(1,i);
   sumf:=sumf+pow*ef/n/n*2;
   sumh:=sumh+pow*eh/n/n*2;
   sumg:=sumg+pow*eg/n/n*2;
   pow:=pow*2;
  end;
 writeln(sumg:0:3,' ',sumh:0:3,' ',sumf:0:3);
end;

begin
 readln(t);
 for i:=1 to t do
  main;
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值