Rainbow的信号
Rainbow的信号-AcWing
简洁题意:
给定一个数列,从中等概率的选取区间\([l,r]~(l>r就交换l,r)\),分别求\(and\)和的期望,\(or\)和的期望,\(xor\)和的期望。
solution:
首先,因为位运算只同每一个二进制位有关系,那么我们可以把每一位分开考虑,求和即可(根据数据范围,最多31位)。
当\(l=r\)时出现的概率:\(\frac{1}{n^2}\)
当\(l\neq r\)时出现的概率:\(\frac{2}{n^2}\)(因为当\(l<r\)时,交换\(l,r\))。
设当前考虑第\(i\)位,第\(j\)个数,\(last_0\)表示\(1到j-1\)中最后一个第\(i\)位为\(0\)的数的下标,\(last_1\)表示\(1到j-1\)中最后一个第\(i\)位为\(1\)的数的下标,\(x_0\)表示\(1到j-1\)中第\(i\)位有偶数个\(1\)的以\(j-1\)结尾的区间数量,\(x_1\)表示\(1到j-1\)中第\(i\)位有奇数个\(1\)的以\(j-1\)结尾的区间数量。
目前统计第\(i\)位中,以第\(j\)的数结尾的所有区间,则\(l=[1,j]~,~r=j\)。
\(and\)和:(\(and\)为1要求\(l-r\)中所有的第\(i\)为都为一)
①,此时考虑到的位上为1:
\(+\frac{1}{n^2}2^i\)(当\(l=r\)时,此时必定为1)。
\(+\frac{2}{n^2}2^i(j-last_0-1)\)。(注意排除掉\(l=r\)的情况)。
②,此时考虑到的位上为0:
不进行任何修改。
\(or\)和:(\(or\)为1要求\(l-r\)中至少有一个数第\(i\)位为1)
①,此时考虑到的位上为1:
\(+\frac{1}{n^2}2^i\)(当\(l=r\)时,此时必定为1)。
\(+\frac{2}{n^2}2^i(j-1)\)。(而此时第\(r\)个数的第\(i\)位就是1,所以都可以。注意排除掉\(l=r\)的情况)。
②,此时考虑到的位上为0:
\(+\frac{2}{n^2}2^i\cdot last_1\)(当\(l=[1,last_1]\)时都至少有一个1)。
\(xor\)和:(\(xor\)和要求\(l-r\)中有奇数个第\(i\)位为1)
①,此时考虑到的位上为1:
\(+\frac{1}{n^2}2^i\)(当\(l=r\)时,此时必定为1)。
\(+\frac{2}{n^2}2^i\cdot x_0\)(因为此时第\(i\)位为1,那么之前偶数个为\(1\)的现在就是奇数个了)
②,此时考虑到的位上为0:
\(+\frac{2}{n^2}2^i\cdot x_1\)
更改\(x_0,x_1\):
①,此时考虑到的位上为1:
先交换\(x_0,x_1\),再将\(x_1+1\)(加上\(l=r=j\)的情况)。
②,此时考虑到的位上为0:
再将\(x_0+1\)(加上\(l=r=j\)的情况)。
代码:
#include<bits/stdc++.h>
#define ld long double
using namespace std;
const int N=1e5+5;
ld ans_and,ans_or,ans_xor;
int n;
int a[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<31;i++){
int last0=0,last1=0,x0=0,x1=0;
ld cnt=(ld)(1<<i);
for(int j=1;j<=n;j++){
int tmp=((a[j]>>i)&1);
if(tmp){
ans_and+=cnt*1.0/n/n*1.0;
ans_and+=cnt*2.0/n/n*1.0*(j-last0-1);
ans_or+=cnt*1.0/n/n*1.0;
ans_or+=cnt*2.0/n/n*1.0*(j-1);
ans_xor+=cnt*1.0/n/n;
ans_xor+=cnt*2.0/n/n*1.0*x0;
last1=j;
swap(x0,x1);
x1++;
}
else{
ans_or+=cnt*2.0/n/n*1.0*last1;
ans_xor+=cnt*2.0/n/n*1.0*x1;
last0=j;
x0++;
}
}
}
printf("%.3Lf %.3Lf %.3Lf",ans_xor,ans_and,ans_or);
return 0;
}

浙公网安备 33010602011771号