題面
題意
給出個數,隨機rand兩個1~n的數,,若,則交換兩數,求區間中的數異或,與,或的值的期望。
做法
首先因爲是位運算,因此我們可以逐位考慮,對每一位單獨計算,下面考慮第位:
可以發現長度爲1的區間被rand到的概率均爲,長度大於1的區間被rand到的概率均爲,因此我們要分開來處理。
對於長度爲1的區間,很好處理,如果這位是1,對三個答案都加上,反之不對答案做出任何貢獻。
對於長度大於1的區間,我們可以枚舉右端點,並記錄0和1上一次出現的位置,然後計算三個答案:
1.與:只有連續的一段1才行,因此答案爲,注意要減去長度爲1的區間的貢獻。
2.或:只要區間中有1即可,因此答案爲。
3.異或:可以發現合法的左端點是相間的區間,每個區間有一些0(可以沒有)和一個1組成,因此用兩個變量分別維護這兩種區間的數的個數,然後每次計算貢獻即可,具體可以參考代碼。
代碼
#include<iostream>
#include<cstdio>
#define db double
#define N 100100
using namespace std;
int n,num[N],tmp[N];
db an1,an2,an3;
inline void solve(int u)
{
int i,j,pos[2],cnt[2];
bool now=0;
db t;
for(i=1;i<=n;i++)
{
if(num[i]&(1 << u))
{
tmp[i]=1;
an1+=(1 << u)*1./n/n;
an2+=(1 << u)*1./n/n;
an3+=(1 << u)*1./n/n;
}
else tmp[i]=0;
}
cnt[0]=cnt[1]=pos[0]=pos[1]=0;
for(i=1;i<=n;i++)
{
pos[tmp[i]]=i;
cnt[now]++;
an1+=(1 << u)*2./n/n*(cnt[now^(!tmp[i])]-tmp[i]);
an2+=(1 << u)*2./n/n*(i-pos[0]-tmp[i]);
an3+=(1 << u)*2./n/n*(pos[1]-tmp[i]);
now^=tmp[i];
}
}
int main()
{
int i,j;
cin>>n;
for(i=1;i<=n;i++) scanf("%d",&num[i]);
for(i=0;i<30;i++) solve(i);
printf("%.3f %.3f %.3f",an1,an2,an3);
}