https://www.acwing.com/problem/content/218/
题意:
Freda发明了传呼机之后,rainbow进一步改进了传呼机发送信息所使用的信号。
由于现在是数字、信息时代,rainbow发明的信号用N个自然数表示。
为了避免两个人的对话被大坏蛋VariantF偷听,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和的平均数。
B部分对话的密码是数列P的and和的期望,定义类似于xor和。
C部分对话的密码是数列P的or和的期望,定义类似于xor和。
请你帮忙计算这三个密码。
输入:
第一行一个正整数N。
第二行N个自然数,表示Freda接到的信号。
输出:
一行三个实数,分别表示xor和、and和、or和的期望,四舍五入保留3位小数,相邻两个实数之间用一个空格隔开。
数据范围:
N<=1e5 N个自然数都不超过1e9
(这道题n*n会爆。。。。。。)
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 7;
//bit : 存放第i个数的第k位 last[0]:上一个第k位为0的数的下标
//last[1]:上一个第k位为1的数的下标
int bit[MAXN], last[5], num[MAXN], belong[3];
double add[40];
int n;
double init_bit(int bit_num) {
double ans = 0.0;
bit_num--;
for (int i = 1; i <= n; i++) {
bit[i] = ((num[i] >> bit_num) & 1);
if (bit[i] == 1) // 长度为1的区间枚举到的可能性为1/n^2
ans += (1.0 / n / n)*add[bit_num];
}
return ans;
}
double and_bit(int bit_num) {
double ans = 0.0;
bit_num--;
last[0] = 0;
for (int i = 1; i <= n; i++) {
if (bit[i] == 1)
ans += (2.0 / n / n)*((double)i - last[0] - 1)*add[bit_num];
else
last[0] = i;
}
return ans;
}
double or_bit(int bit_num) {
double ans = 0.0;
bit_num--;
last[1] = 0;
for (int i = 1; i <= n; i++) {
if (bit[i] == 1) {
ans += (2.0 / n / n)*add[bit_num] * (i - 1);
last[1] = i;
}
else
ans += (2.0 / n / n)*add[bit_num] * last[1];
}
return ans;
}
double xor_bit(int bit_num) {
double ans = 0.0;
bit_num--;
belong[0] = belong[1] = 0; //将总体区间划分为两块,同号合并
int now = 0;
for (int i = 1; i <= n; i++) {
if (bit[i] == 1)
ans += (2.0 / n / n)*belong[now] * add[bit_num];
else
ans += (2.0 / n / n)*belong[now ^ 1] * add[bit_num];
belong[now]++;
if (bit[i] == 1)
now ^= 1;
}
return ans;
}
int main() {
cin >> n;
double and_ans, or_ans, xor_ans;
and_ans = or_ans = xor_ans = 0.0;
add[0] = 1.0;
for (int i = 1; i <= 31; i++)//预处理2的k次方
add[i] = add[i - 1] * 2.0;
for (int i = 1; i <= n; i++)
scanf("%d", num + i);
for (int i = 1; i <= 31; i++) {
double sto = init_bit(i);
and_ans += sto, or_ans += sto;
xor_ans += sto;
and_ans += and_bit(i);
xor_ans += xor_bit(i);
or_ans += or_bit(i);
}
printf("%.3lf %.3lf %.3lf\n", xor_ans, and_ans, or_ans);
return 0;
}