【NOIP2013模拟】Rainbow的信号
Time Limits: 1000 ms Memory Limits: 131072 KB Special Judge
Description
Freda发明了传呼机之后, rainbow 进一步改了传呼机发送信息所使用的号。 由于现在是数字、信息时代, rainbow 发明的信号用 N个自然数表示。 为了避免两个人的对话被 大坏蛋 VariantF偷听 T_T,rainbow 把对话分成 对话分成A、B、C三部分 ,分别用 a、b、c三个密码 加密 。现在 Freda接到了 rainbow的信息,她首要工作就是解密。 Freda了解到,这三部 分的密码计算方式如下:
在 1~N这 N个数 中,等概率地选取两个数l、r,如果 l>r,则交换 l、r。把信号中的第 l个数到第 r个数 取出来,构成一数列 P。
- A部分对话的密码是数列 P的 xor 和的数学 期望值 。xor 和就是 数列 P中各个数异或之 后得到的数 ;xor和的期望 就是对于所有可能选取 的 l、r,所得到的 数列xor和的平均数 。
- A部分对话 占接收到的信息总量 的 40% ,因此 如果你计算出密码 a,将获得该测试点40% 的分数。
- B部分对话的密码是数列 P的 and 和的期望 ,定义类似于 xor和,占信息总 量的 30% 。
- C部分对话的密码是数列 P的 or 和的期望 ,定义类似于 xor 和,占信息总量的 30% 。
Input
第一行 一个正整数 N。
第二行 N个自然数,表示 Freda接到的信号 。
Output
一行三个实数, 分别表示 xor 和、 and 和、 or 和的期望 ,四舍五入保留 3位小数, 相邻 两个 实数之间用不少于一个空格隔开 。三个实数分别占该 测试点 40% 、30% 、30% 的分数, 如果你的输出少于三个实数 ,或者 你的输出不合法,将被判 0分。
Sample Input
Input1:
2
4 5
Input2:
3
1 0 1
Sample Output
Output1:
2.750 4.250 4.750
Output2:
0.667 0.222 0.889
Data Constraint
- 对于 20% 的数据, 1<=N<=100
- 对于 40% 的数据, 1<=N<=1000
- 对于另外 30% 的数据, N个数为 0或 1
- 对于 100% 的数据, 1<=N<=100000 ,N个自然数均不超过 10^9
Hint
l,r | xor | and | or |
---|---|---|---|
1,1 | 4 | 4 | 4 |
1,2 | 1 | 4 | 5 |
2,1 | 1 | 4 | 5 |
2,2 | 5 | 5 | 5 |
以上l,r出现概率均相同,因此分别对xor和,and和,or和求平均数就是数学期望值。
题目大意
给出n个数,Num1 Num2 Num3……Numn
给出 A(i,j) B(i,j) C(i,j) 分别表示从i到j的异或和,and和还有or和。
如果i>j,则 A(i,j)=A(j,i) B(i,j)=B(j,i) C(i,j)=C(j,i)
如果i=j,则 A(i,j)=B(i,j)=C(i,j)=Numi
求:
- ∑ni=1∑nj=1A(i,j)n2
- ∑ni=1∑nj=1B(i,j)n2
- ∑ni=1∑nj=1C(i,j)n2
解题思路
我们可以考虑二进制:
可以想到,将每一位分别求出,在统计起来就可以了。
对于每一位,只有0和1,为了方便理解和讲解,这里用三个数组:
- Fi,表示 ∑nj=1A(j,i)
- Gi,表示 ∑nj=1B(j,i)
- Di,表示 ∑nj=1C(j,i)
那么每一位的答案就是
1.
2∑ni=1Fi−∑ni=1Numin2
2.
2∑ni=1Gi−∑ni=1Numin2
3.
2∑ni=1Di−∑ni=1Numin2
在统计总答案时,第i位的答案要乘以 2i−1
对于求出Fi,Gi,Di。我们先用near1,near0分别表示,不包括当前位,最近的1,0的位置
那么
Fi=F[near1-1]+i-near1 (当前位为1) F[i-1] (当前位为0)
Gi=i-near0 (当前位为1) 0 (当前位为0)
Di=i (当前位为1) near1 (当前位为0)
Codes
var n,i,j,max,nea1,nea0:longint;
ans1,ans2,ans3:real;
num:array[1..100000]of longint;
two:array[0..30]of longint;
bit:array[1..100000,0..31]of shortint;
f,d,g:array[-1..100000]of int64;
procedure doit(w,n:longint);
begin
while n>0 do
begin
inc(bit[w,0]);
bit[w,bit[w,0]]:=n mod 2;
n:=n shr 1;
end;
end;
begin
read(n);
for i:=1 to n do
begin
read(num[i]);
doit(i,num[i]); //转为二进制
if bit[i,0]>max then max:=bit[i,0];
end;
two[0]:=1;
for i:=1 to 30 do two[i]:=two[i-1]*2;
for j:=1 to max do
begin
nea0:=0; nea1:=0;
for i:=1 to n do
begin
if bit[i,j]=0 then //当位为0的情况
begin
f[i]:=f[i-1];
g[i]:=0;
d[i]:=nea1;
nea0:=i;
end else
begin //当位为1情况
f[i]:=f[nea1-1]+i-nea1;
g[i]:=i-nea0;
d[i]:=i;
nea1:=i;
end;
ans1:=ans1+f[i]*two[j]; //计入答案
ans2:=ans2+g[i]*two[j];
ans3:=ans3+d[i]*two[j];
end;
end;
for i:=1 to n do
begin
ans1:=ans1-num[i]; //减去多余部分
ans2:=ans2-num[i];
ans3:=ans3-num[i];
end;
writeln(ans1/n/n:0:3,' ',ans2/n/n:0:3,' ',ans3/n/n:0:3); //求期望,注意,不能/sqr(n) 否则会爆
end.