"蔚来杯"2022牛客暑期多校训练营4 N Particle Arts
题目来源:https://ac.nowcoder.com/acm/contest/33189#question
题意
有 n n n个粒子,第 i i i个粒子的能量为 a i a_i ai,两个能量分别为 a a a和 b b b的粒子碰撞后会消失,并生成两个能量分别为a|b和a&b的粒子。求无限次随机的碰撞后,最后所有粒子的能量的方差。
分析
由题意可得,若两个粒子有不同的二进制位,那么碰撞后相同的位不变,不同位上的所有1都到a|b上,而a&b上原来的不同位的位置都为0,而1和0在每个位上的总数不变。因此在无数次碰撞后,二进制位的1会尽可能的在同个数上。
如数列
1
,
2
,
3
,
4
,
5
1,2,3,4,5
1,2,3,4,5,二进制为
001
,
010
,
011
,
100
,
101
001,010,011,100,101
001,010,011,100,101
无数次碰撞后的数列变为
0
,
0
,
1
,
7
,
7
0,0,1,7,7
0,0,1,7,7,二进制为
000
,
000
,
001
,
111
,
111
000,000,001,111,111
000,000,001,111,111
因此只需记录每位上1的个数,每次在每一位没取完的情况下取出一个1组成新的数列,再用方差公式计算即可。
方差公式:
σ
2
=
1
n
∑
i
=
1
n
a
i
2
−
x
ˉ
2
σ^2=\dfrac{1}{n} \sum\limits_{i=1}^na_i^2-\bar{x}^2
σ2=n1i=1∑nai2−xˉ2
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,sum1,sum2,x,k,ans;
ll a[100005];
ll b[20];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
k=0;
while(x){
b[k]+=x%2;
x/=2;
k++;
}
}
for(int i=1;i<=n;i++){
for(int j=20;j>=0;j--){
if(b[j]){
a[i]=a[i]*2+1;
b[j]--;
}
else a[i]*=2;
}
sum1+=a[i];//总和
sum2+=a[i]*a[i];//平方总和
}
sum1=sum1*sum1;//平均数的平方
sum2=sum2*n;//通分
ans=sum2-sum1;//答案的分子,分母为n*n
ll g=__gcd(ans,n*n);//约分
cout<<ans/g<<"/"<<n*n/g;
}