• bzoj3054 Rainbow的信号 数学期望+位运算


    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和的平均数。
    B部分对话的密码是数列P的and和的期望,定义类似于xor和。
    C部分对话的密码是数列P的or和的期望,定义类似于xor和。

    solution:

    首先考虑把区间长度为1的和区间长度≥2的分开处理。

    长度为1的概率为1/n²,长度≥2的概率为2/n²。

    所以实际上问题就转化为了:求给定序列的每一个区间的or,xor,and和。

    由于位运算是不进位的,所以按位依次考虑每一位。

    假设当前考虑的是第id位,那么把原序列中每一个数的第id位取出来组成一个新的01序列。

    除此之外,用一个last[0/1]记录上一次出现0/1的位置。

    然后考虑从左往右依次扫描这个01序列(记为b[]),如果扫到了第i位,那么:

    1、对于and运算:

    ​ //只有二者都为1才能算贡献

    ​ 如果b[i]==1,那么ansand+=(1<<id)* ((i-1)-(last[0]+1)+1) *2/n/n;

    ​ 如果b[i]==0,显然不需要计算贡献。

    2、对于or运算:

    ​ //有一个1就好了

    ​ 如果b[i]==1,那么ansor+=(1<<id)* (i-1) *2/n/n;

    ​ 如果b[i]==0,那么ansor+=(1<<id)* (last[1]-1) *2/n/n;

    3、对于xor运算:

    ​ 可以发现,能够对xor造成影响是b中的每一个1。

    ​ 这样整个序列就被1划分成了一段一段的区间,要么1,3,5…段有贡献,要么2,4,6…段有贡献。

    ​ 这样的话就可以用两个变量cnt[0/1]分别记录一下上述两种区间的总长度 。

    ​ 如果b[i]==1,那么ansxor+=(1<<id)* cnt[0] *2/n/n;

    ​ 如果b[i]==0,那么ansxor+=(1<<id)* cnt[1] *2/n/n;

    ​ 关于cnt的更新,每次先++cnt[0],如果b[i]==1,swap(cnt[0],cnt[1])即可。

    至此,本题完美解决。

    #include<cmath>
    #include<string>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define RG register
    #define IL inline
    #define LL long long
    #define DB double
    using namespace std;
    
    IL int gi() {
       RG int x=0,w=0; char ch=getchar();
       while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
       while (ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
       return w?-x:x;
    }
    
    const int N=1e5+1;
    
    int n,a[N],b[N];
    DB ansor,ansxor,ansand;
    
    IL void solve(int id) {
    	RG int i,las[2]={0,0},cnt[2]={0,0};
    	for(i=1;i<=n;++i)
    		if((b[i]=(a[i]>>id)&1)) 
    			ansor+=(DB)(1<<id)/n/n,ansxor+=(DB)(1<<id)/n/n,ansand+=(DB)(1<<id)/n/n;
    	for(i=1;i<=n;++i) {
    		if(!b[i]) ansor+=(DB)(1<<id)*las[1]*2/n/n;
    		else ansor+=(DB)(1<<id)*(i-1)*2/n/n,ansand+=(DB)(1<<id)*(i-las[0]-1)*2/n/n;
    		ansxor+=(DB)(1<<id)*cnt[b[i]^1]*2/n/n;
    		++cnt[0],las[b[i]]=i;
    		if(b[i]) swap(cnt[0],cnt[1]);
    	}		
    }
    
    int main()
    {
       	RG int i;
    	for(i=1,n=gi();i<=n;++i) a[i]=gi();
    	for(i=0;i<32;++i) solve(i);
    	printf("%.3lf %.3lf %.3lf
    ",ansxor,ansand,ansor);
        return 0;
    }
    
    
  • 相关阅读:
    HTML--控制小人自由移动
    OC 图片圆角实现
    swift 点击button改变其内填充图片,达到选中的效果
    swift 自定义导航栏颜色
    大道至简第五章阅读笔记
    字符串动手动脑实践及凯撒密码
    大道至简第四章阅读笔记
    类与对象动手动脑实践
    用静态字段和构造函数,查询创建了多少个对象
    大道至简第三章阅读笔记
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10808084.html
Copyright © 2020-2023  润新知