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% 。
Input
第一行 一个正整数 N。
第二行 N个自然数,表示 Freda接到的信号 。
Output
一行三个实数, 分别表示 xor 和、 and 和、 or 和的期望 ,四舍五入保留 3位小数, 相邻 两个 实数之间用不少于一个空格隔开 。三个实数分别占该 测试点 40% 、30% 、30% 的分数, 如果你的输出少于三个实数 ,或者 你的输出不合法,将被判 0分。
Sample Input
输入1:
2
4 5
输入2:
3
1 0 1
Sample Output
输出1:
2.750 4.250 4.750
输出2:
0.667 0.222 0.889
Data Constraint
对于 20% 的数据, 1<=N<=100
对于 40% 的数据, 1<=N<=1000
对于另外 30% 的数据, N个数为 0或 1
对于 100% 的数据, 1<=N<=100000 ,N个自然数均不超过 10^9
Algorithm:位运算、动态规划
由于各位之间互不影响,因此可以分成 31 位分别处理,这样实际上每位只有 0 和 1,就是N 个数要么是 0 要么是 1,求 and/or/xor 和的期望了,最后把各个位合起来就好。然后怎么求 N 个数 (0 或 1) 的 and/or/xor 和的期望呢?
先枚举右端点,然后往左扫描,可以发现一旦遇到一个 0,那么后面的 and 和就都是 0 了。因此对于 and 和,只需要统计每个数左边第一个 0 出现在什么位置!
同理,一旦遇到一个 1,那么后面的 or 和就都是 1 了。
因此对于 or 和,只需要统计每个数左边第一个 1 出现在什么位置!
显然是可以 O(n)统计的,肿么样,or 和、and 和的计算是不是超级简单>_~
对于 xor 和呢?可以发现,枚举右端点然后向左扫描的过程中,遇到 0,xor 和不变,遇到1,xor 和会取反。所以我们以所有的 1 为分界点,把这些数分成几段,要累加的要么就是所有奇数段中的数为左端点的 xor 和之和,要么就是所有偶数段中的数为左端点的 xor 和之和。因此我们只需要两个变量分别记录,枚举右端点的过程中,遇到 1 就交换 x 和 y,然后根据当前情况取 x、y 中合适的一个累加到答案里就可以了~ 这也是 O(n)的。
CODE
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define N 100005
using namespace std;
int n;
double Ans_xor=0,Ans_and=0,Ans_or=0;
int Xor_number[2],Left[2];
int a[N],b[N];
void work(int x)
{
fill(Xor_number,Xor_number+2,0);
fill(Left,Left+2,0);
fo(i,1,n) b[i]=((a[i]>>x)&1);
fo(i,1,n)
{
if (i!=1)
{
Ans_xor+=(double)(1<<x)/n/n*Xor_number[!b[i]];
if (b[i]==0) Ans_or+=(double)(1<<x)/n/n*Left[1];
else
{
Ans_or+=(double)(1<<x)/n/n*(i-1);
Ans_and+=(double)(1<<x)/n/n*(i-1-Left[0]);
}
}
Left[b[i]]=i;
if (b[i]==0) Xor_number[0]++;
else swap(Xor_number[0],Xor_number[1]),Xor_number[1]++;
}
fo(i,1,n)
if (b[i])
{
double tmp=(double)(1<<x)/n/n/2;
Ans_xor+=tmp;
Ans_and+=tmp;
Ans_or+=tmp;
}
}
int main()
{
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
fo(i,0,30) work(i);
printf("%.3lf %.3lf %.3lf\n",Ans_xor*2,Ans_and*2,Ans_or*2);
return 0;
}