1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x38概率與數學期望 Rainbow

《演算法競賽進階指南》0x38概率與數學期望 Rainbow

題目連結:https://www.acwing.com/problem/content/218/

題目給出n個數,要求求從中任意取出一個區間的數求異或和、or和、and和的數學期望。當每個數只有0和1的時候是容易求的,此時只需要把這個int數分解成二進位制數對每一位進行求解即可。

其中異或和的計算中,c1是從r-1開始向前掃描的[l,r-1]段有偶數個1的l取值的集合的大小,c2的定義與 c1相似,只是是奇數個1.如果掃描到當前的數是0,那麼只需要保證c1++,如果當前的數是1的話就需要交換c1和c2並且c2的數量++。

程式碼:

#include<iostream>
#include<cstdio>
using
namespace std; const int maxn = 100010; int a[maxn],b[maxn]; int n; double ansxor,ansor,ansand; void Rainbow(int k){//對每個數的第k位進行處理 int last[2]={0,0},c1=0,c2=0;//last表示上一個0/1在哪個給定的數的第k位出現 double w=(double)(1<<k)/n/n; for(int i=1;i<=n;i++){ b[i]=((a[i]>>k)&1); if(b[i]){//
區間長度為1的情況 ansxor+=w; ansand+=w; ansor+=w; } //區間長度不為1的情況 if(!b[i]){ ansor+=w*last[1]*2; ansxor+=w*c2*2; }else{ ansand+=(i-last[0]-1)*w*2; ansor+=w*(i-1)*2; ansxor+=w*c1*2; }
//為下一位左右右端點的情況計算新的c1,c2 c1++; if(b[i])swap(c1,c2); last[b[i]]=i; } } int main(){ cin>>n; for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=0;i<31;i++)Rainbow(i);//1e9資料之內最多有31位 printf("%.3lf %.3lf %.3lf\n",ansxor,ansand,ansor); return 0; }