选取的概率为,选取的概率为
分别考虑三种运算并按位考虑,考虑第位:
对于的情况显然贡献为为第位为的数的个数。(三种运算的情况都一样)
对于和,记录前面连续的个数,那么当前位置作为右端点,对期望的贡献为
对于和,记录前面第一个有的位置,那么当前位置作为右端点,对期望的贡献为
对于和,记录前缀,并维护两个桶。当当前时,对期望的贡献为;当当前时,对期望的贡献为
这题有点卡精度...要用long double并将除法运算挪到适当的位置...

#include <bits/stdc++.h>
using namespace std;
#define double long double
const int N = 100010;

double ans;
int a[N], n, s[N], cnt[2];

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    ans = 0;
    for(int k = 30; k >= 0; --k) {
        memset(s, 0, sizeof(s));
        cnt[0] = cnt[1] = 0;
        for(int i = 1; i <= n; ++i) {
            if((a[i] >> k) & 1) s[i] = s[i - 1] ^ 1;
            else s[i] = s[i - 1];
            if((a[i] >> k) & 1) ans += (double) (1 << k) / n / n;
        }
        for(int i = 1; i <= n; ++i) {
            ans += (double)(1 << k)  / n / n * cnt[s[i] ^ 1] * 2;
            cnt[s[i - 1]]++;
        }
    }
    printf("%.3Lf ", ans);
    ans = 0;
    for(int k = 30; k >= 0; --k) {
        memset(s, 0, sizeof(s));
        for(int i = 1; i <= n; ++i) {
            if((a[i] >> k) & 1) s[i] = s[i - 1] + 1;
            else s[i] = 0;
            if((a[i] >> k) & 1) ans += (double) (1 << k) / n / n;
        }
        for(int i = 1; i <= n; ++i)
            if((a[i] >> k) & 1) ans += (double)(1 << k)  / n / n * s[i - 1] * 2;
    }
    printf("%.3Lf ", ans);
    ans = 0;
    for(int k = 30; k >= 0; --k) {
        memset(s, 0, sizeof(s));
        for(int i = 1; i <= n; ++i) {
            if((a[i] >> k) & 1) s[i] = i;
            else s[i] = s[i - 1];
            if((a[i] >> k) & 1) ans += (double) (1 << k) / n / n;
        }
        for(int i = 1; i <= n; ++i) {
            if((a[i] >> k) & 1) ans += (double)(1 << k) / n / n * (i - 1) * 2;
            else ans += (double)(1 << k) / n / n * s[i] * 2;
        }
    }
    printf("%.3Lf\n", ans);
}