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;
}
posted @ 2022-12-06 11:52  zyy2001  阅读(125)  评论(0)    收藏  举报