{题解}[jzoj3396] 【NOIP2013模拟】Rainbow的信号

jzoj3396

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=1000000000var
        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.
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值