216. Rainbow的信号
题目链接
216. Rainbow的信号
Freda 发明了传呼机之后,rainbow 进一步改进了传呼机发送信息所使用的信号。
由于现在是数字、信息时代,rainbow 发明的信号用 \(N\) 个自然数表示。
为了避免两个人的对话被大坏蛋 VariantF 偷听,rainbow 把对话分成 \(A、B、C\) 三部分,分别用 \(a、b、c\) 三个密码加密。
现在 Freda 接到了 rainbow 的信息,她的首要工作就是解密。
Freda 了解到,这三部分的密码计算方式如下:
在 \(1 \sim 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\) 位小数,相邻两个实数之间用一个空格隔开。
数据范围
\(1 \le N \le 10^5\),\(N\) 个自然数均不超过 \(10^9\)。
输入样例:
2
4 5
输出样例:
2.750 4.250 4.750
解题思路
期望
对于选择的 \(l,r\),如果 \(l=r\),则对于某个值其选中的概率为 \(\frac{2}{n^2}\),因为如果第一次选中 \(l\),概率为 \(\frac{1}{n}\),第二次再选中 \(l\) 的概率也为 \(\frac{1}{n}\),故其概率为 \(\frac{2}{n^2}\),如果 \(l\neq r\),则对于两个固定的 \(l,r\) ,其概率为 \(\frac{2}{n}\),因为选择的 \(l,r\) 和 \(r,l\) 都算作 \(l,r\),且概率都为 \(\frac{1}{n}\),故其概率都为 \(\frac{2}{n}\),不妨都按 \(\frac{2}{n}\) 来算,最后只用减去 \(l=r\) 的贡献即可,因为 \(xor,and,or\) 都是按位计算,位与位之间互不干扰,所以可以每一位每一位来做,对于 \(xor\),分别统计每个数奇数和偶数的贡献,如果当前数该位为 \(1\),则应该加上前一个数偶数的贡献+1,否则加上前一个数奇数的贡献;对于 \(and\),如果当前数该位为 \(1\),则计算前面有多少个连续的数该位为 \(1\),这样的连续数即为当前数的贡献;对于 \(or\),如果当前数该位为 \(1\),则前面所有数都可以选上,否则,记录前面该位为 \(1\) 且最靠近当前数的位置,该位置即为当前数的贡献
- 时间复杂度:\(O(30n)\)
代码
// Problem: Rainbow的信号
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/218/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,a[N];
LL s[3],sum;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i];
for(int i=0;i<30;i++)
{
int even=0,odd=0;
LL cnt[3]={0};
int add=0;
int one=0;
for(int j=1;j<=n;j++)
if(a[j]>>i&1)
{
cnt[0]+=++even;
swap(even,odd);
cnt[1]+=++add;
cnt[2]+=j;
one=j;
}
else
cnt[0]+=odd,even++,add=0,cnt[2]+=one;
s[0]+=(LL)cnt[0]*(1<<i);
s[1]+=(LL)cnt[1]*(1<<i);
s[2]+=(LL)cnt[2]*(1<<i);
}
printf("%.3lf %.3lf %.3lf",(double)(s[0]*2-sum)/((LL)n*n),(double)(s[1]*2-sum)/((LL)n*n),(double)(s[2]*2-sum)/((LL)n*n));
return 0;
}