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% 。
40分做法:
直接数位DP,
枚举每个数列求值,
更新答案。
100分做法:
在40分基础上多想想,
我们可以再套一个DP
设f[i],g[i],d[i]分别为以第i个数结尾的区间的xor和,and和,or和。
(暂时不考虑期望)
想想。似乎不能直接考虑,需要分位考虑。
再想想。咦!好像和40分做法并没有什么关系呀!
的确没有什么关系。
方程来了!!!
设n1为最近的1(之前),n0为最近的0(之前)
于是就有
多么美妙啊!!!
都很好推,多多思考吧!
Tips
小心精度问题
Code
const
maxn=100000;
maxnum=1000000000;
var
n:longint;
i:longint;
ansa,ansb,ansc:real;
a:array[0..maxn] of longint;
f,g,d:array[-1..maxn] of int64;
x:array[0..maxn,0..32] of byte;
procedure init;
var
i,l:longint;
begin
readln(n);
for i:=1 to n do
begin
read(a[i]);
l:=a[i];
while(l>0)do
begin
inc(x[i,0]);
x[i,x[i,0]]:=l and 1;
l:=l shr 1;
end;
end;
end;
procedure work(o:longint);
var
i:longint;
near0,near1:longint;
begin
near0:=0;near1:=0;
for i:=1 to n do
begin
if x[i,o]=0 then
begin
f[i]:=f[i-1];
g[i]:=0;
d[i]:=near1;
near0:=i;
end
else
if x[i,o]=1 then
begin
f[i]:=f[near1-1]+(i-near1);
g[i]:=i-near0;
d[i]:=i;
near1:=i;
end;
ansa:=ansa+(f[i] shl o);
ansb:=ansb+(g[i] shl o);
ansc:=ansc+(d[i] shl o);
end;
end;
begin
init;
for i:=1 to 32 do
work(i);
for i:=1 to n do
begin
ansa:=ansa-a[i];
ansb:=ansb-a[i];
ansc:=ansc-a[i];
end;
writeln(ansa/n/n:0:3,' ',ansb/n/n:0:3,' ',ansc/n/n:0:3);
end.