题目
题目大意
给定一个数组,数组的任意两个元素按位与(&)和按位或(|),并将数组的那两个元素替换成按位与(&)和按位或(|)的运算结果,一直这样操作下去,直至数组的每个元素都不变,求出其方差,并按照题目要求的格式输出。
分析
通过观察可以发现一下几个特性
1、任意两个元素进行&和|操作时,元素每一位的1和0的个数是不变的
比如10和11 101&110=100 101|110=111
一开始是101和110,运算后变成了100和111
每一位的0的数量和1的数量还是相等的,最高位都是两个1,中间位都是1个0和1个1,最低位都是1个0和1个1。
2、知道了每一位的01个数相等,那么可以推出转变完的序列和转变前的序列和相等
3、与运算就相当于把0聚集到一个数身上,或运算相当于把1聚集到一个数身上
那么可以想到先计算出每一位的1的个数,1尽量分配到一起,如果那一位没有1分配,那么用0来,以此类推。
方差的公式通过数学可以转化为
n
(
∑
i
=
1
n
x
2
)
−
s
u
m
2
n
2
\frac{n(\sum_{i=1}^nx^2)-sum^2}{n^2}
n2n(∑i=1nx2)−sum2
代码
#include<bits/stdc++.h>
using namespace std;
#define double long double
typedef long long ll;
const ll mod=1e9+7;
const ll inf=0x3f3f3f3f;
const double eps=1e-10;
ll a[100009];
ll num[20];
void judge(ll x)
{
ll flag=0;
while(x)
{
if(x&1) num[flag]++;
x/=2;
flag++;
}
}
ll get()
{
ll ans=0;
for(ll i=0;i<=17;i++)
{
if(num[i])
{
ans+=(1<<i);
num[i]--;
}
}
return ans;
}
void solve()
{
ll n,i,j,sum=0;
cin>>n;
for(i=1;i<=n;i++) cin>>a[i],sum+=a[i];
for(i=1;i<=n;i++)
{
judge(a[i]);
}
for(i=1;i<=n;i++)
{
a[i]=get();
}
ll tmp=0;
for(i=1;i<=n;i++) tmp=tmp+a[i]*a[i];
tmp=n*tmp-sum*sum;
if(tmp==0) cout<<"0/1"<<endl;
else
{
ll g=__gcd(tmp,n*n);
cout<<tmp/g<<"/"<<n*n/g<<endl;
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
//cin>>t;
while(t--)
{
solve();
}
return 0;
}