题意
题解
位运算是不进位的,各位之间互不影响。因此,我们可以把 N N N 个自然数分成 30 30 30 位,对每一位分别进行处理。
等概率地选取两个数作为区间端点,那么长度为 1 1 1 的区间,选取的概率为 1 / N 2 1/N^2 1/N2;长度大于 1 1 1 的区间,选取的概率为 2 / N 2 2/N^2 2/N2。问题转化为求解数列第 k k k 位在 x o r , a n d , o r xor,and,or xor,and,or 运算下的长度等于 1 1 1 与长度大于 1 1 1 的区间和。
a n d and and 运算出现 0 0 0 时数列和只能为 0 0 0, o r or or 运算出现 1 1 1 时数列和只能为 1 1 1;根据上述性质,枚举区间右界,使用 l s t [ 2 ] lst[2] lst[2] 维护数字 0 , 1 0,1 0,1 在数列中出现的小于当前枚举右界的最大索引,同时统计答案。
x o r xor xor 运算在当前操作数为 1 1 1 时结果取反,若固定右界,那么区间左界每向左移动遇到 1 1 1 则 x o r xor xor 和取反,那么将数列中被 1 1 1 分隔的区间按照奇偶性编号,此时奇数编号的区间 x o r xor xor 和相等,偶数编号区间亦然。设 s u m [ i ] sum[i] sum[i] 为数列区间 [ 1 , i ] [1,i] [1,i] 中这样的奇数段长度,在移动右界的同时进行维护并更新答案。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005, maxlg = 30;
int N, A[maxn], B[maxlg][maxn], sum[maxn], lst[2];
int main()
{
scanf("%d", &N);
for (int i = 1; i <= N; ++i)
scanf("%d", A + i);
double res = 0, bs = 1.0 / N / N;
for (int i = 0; i < maxlg; ++i)
for (int j = 1; j <= N; ++j)
B[i][j] = A[j] >> i & 1;
for (int i = 0; i < maxlg; ++i)
{
sum[0] = 0, lst[0] = lst[1] = 0;
double x = bs * (1 << i);
for (int j = 1; j <= N; ++j)
{
if (B[i][j])
sum[j] = j - lst[1] + lst[1] - sum[lst[1]], res += (2 * (sum[j] - 1) + 1) * x;
else
sum[j] = sum[j - 1], res += 2 * sum[j] * x;
lst[B[i][j]] = j;
}
}
printf("%.3f ", res), res = 0;
for (int i = 0; i < maxlg; ++i)
{
lst[0] = lst[1] = 0;
double x = bs * (1 << i);
for (int j = 1; j <= N; ++j)
{
if (B[i][j])
res += (2 * (j - 1 - lst[0]) + 1) * x;
lst[B[i][j]] = j;
}
}
printf("%.3f ", res), res = 0;
for (int i = 0; i < maxlg; i++)
{
lst[0] = lst[1] = 0;
double x = bs * (1 << i);
for (int j = 1; j <= N; ++j)
{
if (B[i][j])
res += (2 * (j - 1) + 1) * x;
else
res += 2 * lst[1] * x;
lst[B[i][j]] = j;
}
}
printf("%.3f\n", res);
return 0;
}