題目傳送門
【題目大意】
給定乙個有n個數的數列,在這n個數中等概率的選取兩個數l,r(保證l≤r),組成乙個區間[l,r],求出區間的每個數異或之後的結果,求對於這個數列的異或的期望值(此處定義為所有區間異或的結果的平均數)。類似地還要求出與運算和或運算的期望值。
【思路分析】
這題乍一看有點複雜呀,其實就是乙個披著概率的皮的位運算題。
既然是位運算,那就很簡單啦。因為位運算是不會出現進製的,也就是說每一位之間互不影響,所以就可以拆開之後一位一位地處理。首先把數列a中的每個數都轉成二進位制,然後b[i]表示a[i]第k位的數,此處的k會在程式執行過程中列舉。
然後我們發現對於任意區間,只可能存在兩種情況:長度=1或長度≥2。
分類討論發現,區間長度=1時,期望值就是乘上$\frac$,而區間長度≥2時,期望值就是乘上$\frac$
接下來對於三種運算我們分別分析一下(相對二進位制每一位來說)
1.與運算
根據與運算的特點可知,只要區間內出現了0,那麼整個區間的結果就一定是0
2.或運算
同樣根據特點可知,只要區間內出現了1,那麼整個區間的結果就一定是1
3.異或運算
異或運算比較麻煩,我們需要找到區間內每個1的位置,然後一旦遇到1,結果就會變化(由1變成0或者由0變成1),所以可以以1為分界線把整個區間分割成幾塊,然後再具體處理
為了方便實現,我們列舉右端點,用last[0]和last[1]分別記錄上乙個出現0和1的位置,然後用c1和c2分別記錄當前情況下可以使異或結果為1和0的左端點個數。
這裡要注意一點,當我們列舉到乙個新的右端點的時候,要根據這一點是1或0分類討論,詳見**。
還有乙個要注意的地方就是不能直接/(n*n),而要寫成/n/n,我也不知道為什麼但我就是被這個地方卡了好久,也許是精度問題?qaq
【**實現】
1 #include2**戳這裡#define go(i,a,b) for(register int i=a;i<=b;i++)
3using
namespace
std;
4double
ans_and,ans_or,ans_xor;
5int a[1000002],b[1000002
],n;
6void work(int k),c1=0,c2=0
;8 go(i,1
,n)14
}15 go(i,1,n)
22 ans_xor+=(1
<2.0/n/n*(b[i]?c1:c2);
23 c1++;if
(b[i]) swap(c1,c2);
24 last[b[i]]=i;25}
26}27int
main()
NOIP2013模擬 Rainbow的訊號
time limits 1000 ms memory limits 131072 kb special judge description freda發明了傳呼機之後,rainbow 進一步改了傳呼機傳送資訊所使用的號。由於現在是數字 資訊時代,rainbow 發明的訊號用 n個自然數表示。為了避免...
NOIP2013模擬 Rainbow的訊號
正解 類數字dp,統計二進位制下0和一的個數,blablabla。具體不懂,要再問問,懂了再更。我們可以考慮二進位制 可以想到,將每一位分別求出,在統計起來就可以了。var n,m,tot0,tot1 int64 i,j,l,k longint ans1,ans2,ans3 int64 a arra...
Luogu 4998 訊號塔 題解報告
給定乙個數軸,以及數軸上的 n 個點 這些點可能坐落在同一座標上 第 i 個點的座標為 a i 現在要在數軸上找 k 個點,第 i 個點的座標為 x i 求這 k 個點到原數軸上 n 個點距離和的最小值,即 min sum sum x i a j 注 n,a i leq 10 6,k leq n 首...