AcWing 216 数学期望

题意

传送门 AcWing 216 Rainbow的信号

题解

位运算是不进位的,各位之间互不影响。因此,我们可以把 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值