Rainbow的信号
1s,128 MB1s,128\ MB1s,128 MB
【题目描述】
FredaFredaFreda 发明了传呼机之后,rainbowrainbowrainbow 进一步改进了传呼机发送信息所使用的信号。由于现在是数字、信息时代,rainbowrainbowrainbow 发明的信号用 NNN 个自然数表示。为了避免两个人的对话被 大坏蛋 VariantFVariantFVariantF 偷听,rainbowrainbowrainbow 把对话分成 A、B、CA 、B 、CA、B、C 三部分,分别用 a、b、ca、b、ca、b、c 三个密码 加密。现在 FredaFredaFreda 接到了 rainbowrainbowrainbow 的信息,她的首要工作就是解密。FredaFredaFreda 了解到,这三部 分的密码计算方式如下:
在 1∼N1\sim N1∼N 这 NNN 个数中,等概率地选取两个数 l、rl、rl、r,如果 l>rl>rl>r,则交换 l、rl、rl、r。把信号中的第 lll 个数到第 rrr 个数取出来,构成一个数列 PPP 。
AAA 部分对话的密码是数列 PPP 的 xorxorxor 和的数学期望值。xorxorxor 和就是数列 PPP 中各个数异或之后得到的数; xorxorxor 和的期望就是对于所有可能选取的 l、rl、 rl、r,所得到的数列的 xorxorxor 和的平均数。
BBB 部分对话的密码是数列 PPP 的 andandand 和的期望,定义类似于 xorxorxor 和 。
CCC 部分对话的密码是数列 P 的 ororor 和的期望,定义类似于 xorxorxor 和 。
【输入格式】
第一行一个正整数 NNN 。
第二行 NNN 个自然数,表示 FredaFredaFreda 接到的信号。
【输出格式】
一行三个实数,分别表示 xorxorxor 和、andandand 和、ororor 和的期望,四舍五入保留 333 位小数,相邻两个实数之间用不少于一个空格隔开。三个实数分别占该测试点 40%、30%、30%40\%、30\%、30\%40%、30%、30% 的分数, 如果你的输出少于三个实数,或者你的输出不合法,将被判 000 分。
【样例输入】
2
4 5
【样例输出】
2.750 4.250 4.750
【样例解释】
包含共四种可能的 l,rl,rl,r :
l,rl,rl,r | xorxorxor 和 | andandand 和 | ororor 和 |
---|---|---|---|
1,11,11,1 | 444 | 444 | 444 |
1,21,21,2 | 111 | 444 | 555 |
2,12,12,1 | 111 | 444 | 555 |
2,22,22,2 | 555 | 555 | 555 |
以上每一对 l,rl,rl,r 出现的概率相同,因此分别对 xor、and、orxor、and、orxor、and、or 和取平均数,就是数学期望值。
【数据范围】
对于 20%20\%20% 的数据,1≤N≤1001\le N\le 1001≤N≤100 。
对于 40%40\%40% 的数据,1≤N≤10001\le N\le 10001≤N≤1000 。
对于另外 30%30\%30% 的数据,NNN 个数为 000 或 111 。
对于 100%100\%100% 的数据,1≤N≤1000001\le N\le 1000001≤N≤100000 ,NNN 个自然数均不超过 10910^9109 。
【算法分析】
对于位运算,最后的结果只和参与运算的当前位的值有关,因此我们可以单独考虑每一位的结果,将每一位对结果的贡献相加,就得到了最后的答案。
将 1∼n1\sim n1∼n 转换为二进制,根据题意,转化的二进制不会超过 313131 位,则依次计算 1∼n1\sim n1∼n 的二进制第 000 位,第 111 位… 第 303030 位进行位运算的值。
for(int i=0;i<31;i++) //二进制位
for(int j=1;j<=n;j++) // n个数的二进制第i位进行位运算
//......
l,rl,rl,r 出现的概率相同,因此,l==rl==rl==r 出现的概率为 1n2\frac 1 {n^2}n21 ,l≠rl\neq rl=r 出现的概率为 2n2\frac 2 {n^2}n22 。
考虑 nnn 个数的第 kkk 位二进制的位运算,设 a[i][j]a[i][j]a[i][j] 表示第 iii 个数二进制的第 jjj 位的值。
andandand 和的期望:
andandand 的特点就是只要出现 000 结果就为 000 ,只有全为 111 时,结果才是 111 。依次计算前 rrr 个数的 andandand 和的期望,同时记录前 rrr 个数中 000 最后一次出现的位置,last[0]=ilast[0]=ilast[0]=i 。
若第 rrr 个数的第 kkk 位为 a[r][k]=1a[r][k]=1a[r][k]=1 ,以 rrr 为右端点, [l=last[0]+1,r][l=last[0]+1, r][l=last[0]+1,r] 这个区间任意选两个数组成的区间 andandand 运算都是 111 ,则对最后 andandand 和的期望的贡献为:
if(last[0]+1==r)
ans_and+=2k×1n2ans\_and+=2^k\times \frac 1 {n^2}ans_and+=2k×n21
if(last[0]+1<r)
ans_and+=2k×(r−(last[0]+1))×2n2ans\_and+=2^k\times (r-(last[0]+1))\times \frac 2 {n^2}ans_and+=2k×(r−(last[0]+1))×n22
若第 rrr 个数的第 kkk 位为 a[r][k]=0a[r][k]=0a[r][k]=0 ,那么不存在 rrr 为右端点的组合使得第 kkk 位二进制进行 andandand 运算结果为 111 。
ororor 和的期望:
ororor 的特点就是只要有 111 ,最后的结果就是 111 ,只有全为 000 ,结果才是 000 。记录前 rrr 个数中 111 最后一次出现的位置,last[1]=ilast[1]=ilast[1]=i 。
若第 rrr 个数的第 kkk 位为 a[k][r]=1a[k][r]=1a[k][r]=1,以 rrr 为右端点,前面的所有点都可以和 rrr 组成区间满足区间的数 ororor 运算为 111 ,则对最后 ororor 和的期望的贡献为:
[l,r],l=r[l,r],l=r[l,r],l=r and_or+=2k×1n2and\_or+=2^k\times \frac 1 {n^2}and_or+=2k×n21
[l,r],1≤l<r[l,r],1\le l<r[l,r],1≤l<r and_or+=2k×(r−1)×2n2and\_or+=2^k\times (r-1)\times \frac 2 {n^2}and_or+=2k×(r−1)×n22
若第 rrr 个数的第 kkk 位为 a[r][k]=0a[r][k]=0a[r][k]=0 ,那么 rrr 为右端点,l(1≤l≤last[1])l(1\le l\le last[1])l(1≤l≤last[1]) 组成的区间的数 ororor 运算为 111 ,则对最后 ororor 和的期望的贡献为:
and_or+=2k×last[1]×2n2and\_or+=2^k\times last[1]\times \frac 2 {n^2}and_or+=2k×last[1]×n22
xorxorxor 和的期望:
如果 l==r
,设 l xor r=xl\ xor \ r=xl xor r=x 。将 lll 往左扫描,遇到 000 ,区间 [l,r][l,r][l,r] 的 xorxorxor 和不变,遇到 111 ,区间 [l,r][l,r][l,r] 的 xorxorxor 和取反。
如下图,当第 rrr 个数的第 kkk 位为 a[r][k]=0a[r][k]=0a[r][k]=0 时,当 lll 取灰色的位置时,区间 [l,r][l,r][l,r] 的 xorxorxor 和为 111 ,白色的位置时,区间 [l,r][l,r][l,r] 的 xorxorxor 和为 000 。
因此,建立两个变量 c1,c2c_1,c_2c1,c2 分别表示第从 r−1r-1r−1 倒着往前数的白色区域和灰色区域的长度。
当 a[r][k]=0a[r][k]=0a[r][k]=0 时,则左端点 lll 只能选择灰色的部分,对最后 xorxorxor 和的贡献的期望为:
ans_xor+=2k×c2×2n2ans\_xor+=2^k\times c_2\times \frac 2 {n^2}ans_xor+=2k×c2×n22
当 a[r][k]=1a[r][k]=1a[r][k]=1 时,则左端点 lll 只能选择白色的部分,对最后 xorxorxor 和的贡献的期望为:
ans_xor+=2k×c1×2n2ans\_xor+=2^k\times c_1\times \frac 2 {n^2}ans_xor+=2k×c1×n22
同时,需要加上 l==r
的情况,and_xor+=2k×1n2and\_xor+=2^k\times \frac 1 {n^2}and_xor+=2k×n21
将 rrr 所在的区域一直看作白色,左侧依次为灰色,白色,灰色…
计算 c1,c2c_1,c_2c1,c2 时,当 rrr 增加 111 ,c1++
,当 a[r][k]=1a[r][k]=1a[r][k]=1 ,c1c1c1 变为灰色区域长度,c2c2c2 变为白色区域长度,交换 swap(c1,c2)swap(c_1,c_2)swap(c1,c2) 。
总的时间复杂度为 O(kN)O(kN)O(kN) ,kkk 为二进制位数。
在进行位运算时,可以不用将每个数拆分成二进制,可以直接使用位运算提前二进制下的每一位的值,xxx 的二进制为从有右往左依次为第 000 位,…,提取 xxx 第 kkk 的值:(x>>k)&1
【参考程序】
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],n,last[2];
long double ans_xor,ans_or,ans_and;
void solve()
{
for(int i=0;i<31;i++)
{
last[1]=last[0]=0;
int c1=0,c2=0;
for(int j=1;j<=n;j++)
{
int t=(a[j]>>i)&1;
if(t==1)
{
ans_or+=(1<<i)*2.0/n/n*(j-1);
ans_or+=(1<<i)*1.0/n/n;
ans_and+=(1<<i)*2.0/n/n*(j-(last[0]+1));
ans_and+=(1<<i)*1.0/n/n;
ans_xor+=(1<<i)*2.0/n/n*c1;
ans_xor+=(1<<i)*1.0/n/n;
}
else
{
ans_or+=(1<<i)*2.0/n/n*last[1];
ans_xor+=(1<<i)*2.0/n/n*c2;
}
last[t]=j;
c1++;
if(t==1) swap(c1,c2);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
solve();
printf("%.3Lf %.3Lf %.3Lf",ans_xor,ans_and,ans_or);
return 0;
}