ch3801 AcWing 216 Rainbow的信号 期望与位运算

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值