Rainbow的信号
1 s , 128 M B 1s,128\ MB 1s,128 MB
【题目描述】
F r e d a Freda Freda 发明了传呼机之后, r a i n b o w rainbow rainbow 进一步改进了传呼机发送信息所使用的信号。由于现在是数字、信息时代, r a i n b o w rainbow rainbow 发明的信号用 N N N 个自然数表示。为了避免两个人的对话被 大坏蛋 V a r i a n t F VariantF VariantF 偷听, r a i n b o w rainbow rainbow 把对话分成 A 、 B 、 C A 、B 、C A、B、C 三部分,分别用 a 、 b 、 c a、b、c a、b、c 三个密码 加密。现在 F r e d a Freda Freda 接到了 r a i n b o w rainbow rainbow 的信息,她的首要工作就是解密。 F r e d a Freda Freda 了解到,这三部 分的密码计算方式如下:
在 1 ∼ N 1\sim N 1∼N 这 N N N 个数中,等概率地选取两个数 l 、 r l、r l、r,如果 l > r l>r l>r,则交换 l 、 r l、r l、r。把信号中的第 l l l 个数到第 r r r 个数取出来,构成一个数列 P P P 。
A A A 部分对话的密码是数列 P P P 的 x o r xor xor 和的数学期望值。 x o r xor xor 和就是数列 P P P 中各个数异或之后得到的数; x o r xor xor 和的期望就是对于所有可能选取的 l 、 r l、 r l、r,所得到的数列的 x o r xor xor 和的平均数。
B B B 部分对话的密码是数列 P P P 的 a n d and and 和的期望,定义类似于 x o r xor xor 和 。
C C C 部分对话的密码是数列 P 的 o r or or 和的期望,定义类似于 x o r xor xor 和 。
【输入格式】
第一行一个正整数 N N N 。
第二行 N N N 个自然数,表示 F r e d a Freda Freda 接到的信号。
【输出格式】
一行三个实数,分别表示 x o r xor xor 和、 a n d and and 和、 o r or or 和的期望,四舍五入保留 3 3 3 位小数,相邻两个实数之间用不少于一个空格隔开。三个实数分别占该测试点 40 % 、 30 % 、 30 % 40\%、30\%、30\% 40%、30%、30% 的分数, 如果你的输出少于三个实数,或者你的输出不合法,将被判 0 0 0 分。
【样例输入】
2
4 5
【样例输出】
2.750 4.250 4.750
【样例解释】
包含共四种可能的 l , r l,r l,r :
l , r l,r l,r | x o r xor xor 和 | a n d and and 和 | o r or or 和 |
---|---|---|---|
1 , 1 1,1 1,1 | 4 4 4 | 4 4 4 | 4 4 4 |
1 , 2 1,2 1,2 | 1 1 1 | 4 4 4 | 5 5 5 |
2 , 1 2,1 2,1 | 1 1 1 | 4 4 4 | 5 5 5 |
2 , 2 2,2 2,2 | 5 5 5 | 5 5 5 | 5 5 5 |
以上每一对 l , r l,r l,r 出现的概率相同,因此分别对 x o r 、 a n d 、 o r xor、and、or xor、and、or 和取平均数,就是数学期望值。
【数据范围】
对于 20 % 20\% 20% 的数据, 1 ≤ N ≤ 100 1\le N\le 100 1≤N≤100 。
对于 40 % 40\% 40% 的数据, 1 ≤ N ≤ 1000 1\le N\le 1000 1≤N≤1000 。
对于另外 30 % 30\% 30% 的数据, N N N 个数为 0 0 0 或 1 1 1 。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 100000 1\le N\le 100000 1≤N≤100000 , N N N 个自然数均不超过 1 0 9 10^9 109 。
【算法分析】
对于位运算,最后的结果只和参与运算的当前位的值有关,因此我们可以单独考虑每一位的结果,将每一位对结果的贡献相加,就得到了最后的答案。
将 1 ∼ n 1\sim n 1∼n 转换为二进制,根据题意,转化的二进制不会超过 31 31 31 位,则依次计算 1 ∼ n 1\sim n 1∼n 的二进制第 0 0 0 位,第 1 1 1 位… 第 30 30 30 位进行位运算的值。
for(int i=0;i<31;i++) //二进制位
for(int j=1;j<=n;j++) // n个数的二进制第i位进行位运算
//......
l , r l,r l,r 出现的概率相同,因此, l = = r l==r l==r 出现的概率为 1 n 2 \frac 1 {n^2} n21 , l ≠ r l\neq r l=r 出现的概率为 2 n 2 \frac 2 {n^2} n22 。
考虑 n n n 个数的第 k k k 位二进制的位运算,设 a [ i ] [ j ] a[i][j] a[i][j] 表示第 i i i 个数二进制的第 j j j 位的值。
a n d and and 和的期望:
a n d and and 的特点就是只要出现 0 0 0 结果就为 0 0 0 ,只有全为 1 1 1 时,结果才是 1 1 1 。依次计算前 r r r 个数的 a n d and and 和的期望,同时记录前 r r r 个数中 0 0 0 最后一次出现的位置, l a s t [ 0 ] = i last[0]=i last[0]=i 。
若第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 1 a[r][k]=1 a[r][k]=1 ,以 r r r 为右端点, [ l = l a s t [ 0 ] + 1 , r ] [l=last[0]+1, r] [l=last[0]+1,r] 这个区间任意选两个数组成的区间 a n d and and 运算都是 1 1 1 ,则对最后 a n d and and 和的期望的贡献为:
if(last[0]+1==r)
a
n
s
_
a
n
d
+
=
2
k
×
1
n
2
ans\_and+=2^k\times \frac 1 {n^2}
ans_and+=2k×n21
if(last[0]+1<r)
a
n
s
_
a
n
d
+
=
2
k
×
(
r
−
(
l
a
s
t
[
0
]
+
1
)
)
×
2
n
2
ans\_and+=2^k\times (r-(last[0]+1))\times \frac 2 {n^2}
ans_and+=2k×(r−(last[0]+1))×n22
若第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 ,那么不存在 r r r 为右端点的组合使得第 k k k 位二进制进行 a n d and and 运算结果为 1 1 1 。
o r or or 和的期望:
o r or or 的特点就是只要有 1 1 1 ,最后的结果就是 1 1 1 ,只有全为 0 0 0 ,结果才是 0 0 0 。记录前 r r r 个数中 1 1 1 最后一次出现的位置, l a s t [ 1 ] = i last[1]=i last[1]=i 。
若第 r r r 个数的第 k k k 位为 a [ k ] [ r ] = 1 a[k][r]=1 a[k][r]=1,以 r r r 为右端点,前面的所有点都可以和 r r r 组成区间满足区间的数 o r or or 运算为 1 1 1 ,则对最后 o r or or 和的期望的贡献为:
[ l , r ] , l = r [l,r],l=r [l,r],l=r a n d _ o r + = 2 k × 1 n 2 and\_or+=2^k\times \frac 1 {n^2} and_or+=2k×n21
[ l , r ] , 1 ≤ l < r [l,r],1\le l<r [l,r],1≤l<r a n d _ o r + = 2 k × ( r − 1 ) × 2 n 2 and\_or+=2^k\times (r-1)\times \frac 2 {n^2} and_or+=2k×(r−1)×n22
若第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 ,那么 r r r 为右端点, l ( 1 ≤ l ≤ l a s t [ 1 ] ) l(1\le l\le last[1]) l(1≤l≤last[1]) 组成的区间的数 o r or or 运算为 1 1 1 ,则对最后 o r or or 和的期望的贡献为:
a n d _ o r + = 2 k × l a s t [ 1 ] × 2 n 2 and\_or+=2^k\times last[1]\times \frac 2 {n^2} and_or+=2k×last[1]×n22
x o r xor xor 和的期望:
如果 l==r
,设
l
x
o
r
r
=
x
l\ xor \ r=x
l xor r=x 。将
l
l
l 往左扫描,遇到
0
0
0 ,区间
[
l
,
r
]
[l,r]
[l,r] 的
x
o
r
xor
xor 和不变,遇到
1
1
1 ,区间
[
l
,
r
]
[l,r]
[l,r] 的
x
o
r
xor
xor 和取反。
如下图,当第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 时,当 l l l 取灰色的位置时,区间 [ l , r ] [l,r] [l,r] 的 x o r xor xor 和为 1 1 1 ,白色的位置时,区间 [ l , r ] [l,r] [l,r] 的 x o r xor xor 和为 0 0 0 。
因此,建立两个变量 c 1 , c 2 c_1,c_2 c1,c2 分别表示第从 r − 1 r-1 r−1 倒着往前数的白色区域和灰色区域的长度。
当 a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 时,则左端点 l l l 只能选择灰色的部分,对最后 x o r xor xor 和的贡献的期望为:
a n s _ x o r + = 2 k × c 2 × 2 n 2 ans\_xor+=2^k\times c_2\times \frac 2 {n^2} ans_xor+=2k×c2×n22
当 a [ r ] [ k ] = 1 a[r][k]=1 a[r][k]=1 时,则左端点 l l l 只能选择白色的部分,对最后 x o r xor xor 和的贡献的期望为:
a n s _ x o r + = 2 k × c 1 × 2 n 2 ans\_xor+=2^k\times c_1\times \frac 2 {n^2} ans_xor+=2k×c1×n22
同时,需要加上 l==r
的情况,
a
n
d
_
x
o
r
+
=
2
k
×
1
n
2
and\_xor+=2^k\times \frac 1 {n^2}
and_xor+=2k×n21
将 r r r 所在的区域一直看作白色,左侧依次为灰色,白色,灰色…
计算
c
1
,
c
2
c_1,c_2
c1,c2 时,当
r
r
r 增加
1
1
1 ,c1++
,当
a
[
r
]
[
k
]
=
1
a[r][k]=1
a[r][k]=1 ,
c
1
c1
c1 变为灰色区域长度,
c
2
c2
c2 变为白色区域长度,交换
s
w
a
p
(
c
1
,
c
2
)
swap(c_1,c_2)
swap(c1,c2) 。
总的时间复杂度为 O ( k N ) O(kN) O(kN) , k k k 为二进制位数。
在进行位运算时,可以不用将每个数拆分成二进制,可以直接使用位运算提前二进制下的每一位的值,
x
x
x 的二进制为从有右往左依次为第
0
0
0 位,…,提取
x
x
x 第
k
k
k 的值:(x>>k)&1
【参考程序】
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],n,last[2];
long double ans_xor,ans_or,ans_and;
void solve()
{
for(int i=0;i<31;i++)
{
last[1]=last[0]=0;
int c1=0,c2=0;
for(int j=1;j<=n;j++)
{
int t=(a[j]>>i)&1;
if(t==1)
{
ans_or+=(1<<i)*2.0/n/n*(j-1);
ans_or+=(1<<i)*1.0/n/n;
ans_and+=(1<<i)*2.0/n/n*(j-(last[0]+1));
ans_and+=(1<<i)*1.0/n/n;
ans_xor+=(1<<i)*2.0/n/n*c1;
ans_xor+=(1<<i)*1.0/n/n;
}
else
{
ans_or+=(1<<i)*2.0/n/n*last[1];
ans_xor+=(1<<i)*2.0/n/n*c2;
}
last[t]=j;
c1++;
if(t==1) swap(c1,c2);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
solve();
printf("%.3Lf %.3Lf %.3Lf",ans_xor,ans_and,ans_or);
return 0;
}