1. 程式人生 > 實用技巧 >【清華集訓2014】瑪裡苟斯

【清華集訓2014】瑪裡苟斯

UOJ36【清華集訓2014】瑪裡苟斯

給定序列 \(a\),每個元素有 \(\frac{1}{2}\) 的概率被選擇,設 \(x\) 表示被選擇的元素的異或和,求 \(x^k\) 的期望。

\(n\le 10^5,k\le 5,a_i\ge 0\)

保證答案小於 \(2^{63}\)

Solution

假設 \(k\ge 3\),那麼可以注意到值域會大概是 \(\sqrt{2^{63}}\) 這個級別。

所以線性基很小,由於每種值的出現次數都相同(線性基的推論)所以每種值對答案的貢獻都是 \(\frac{x^k}{2^{cnt}}\)

否則如果 \(k=1\),那麼只需要考慮每一位的貢獻。

\(k=2\)

就考慮一下每兩位的貢獻即可。

當然,仍然只需要保留線性基的元素,這樣的話就不會除非常大的數。

然後這個題有結論,答案最多隻能是 \(x.5\),所以特判一下即可

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define int __int128
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int N = 1e5 + 5 ; 
const int M = 100 + 5 ; 
int n, K, cnt, tmp[M], s[M], C[M][M], fc[150] ;
int Ans ; 
void insert(int x) {
	drep( i, 0, 64 ) if((x >> i) & 1) {
		if( tmp[i] ) x ^= tmp[i] ;
		else tmp[i] = x, x = 0 ;
	}
}
int fpow(int x, int k) {
	int ans = 1, base = x ;
	while(k) {
		if(k & 1) ans = ans * base ;
		base = base * base, k >>= 1 ;
	} return ans ;
}
void Dfs(int x, int S) {
	if(x == cnt + 1) return Ans += fpow(S, K), void() ; 
	Dfs(x + 1, S), Dfs(x + 1, S ^ s[x]) ;
}
void write() {
	int iv = (1ll << cnt) ; 
	int d = Ans / iv ; 
	printf("%llu", (unsigned long long)(d) ) ;
	if( d * iv < Ans ) printf(".5") ; 
}
void Solve1() {
	Dfs(1, 0), write() ; 
}
void Solve2() {
	fc[0] = 1 ; rep( i, 1, 100 ) fc[i] = fc[i - 1] * 2 ; 
	rep( i, 0, 62 ) rep( j, 0, 62 ) {
		if( i + j > 100 ) continue ; 
		int a = 0, b = 0, c = 0, r = 0, d = fc[i + j] ;
		rep( k, 1, cnt ) if((s[k] >> i) & 1) ++ a ;
		rep( k, 1, cnt ) if((s[k] >> j) & 1) ++ b ;
		rep( k, 1, cnt ) if(((s[k] >> i) & 1) && ((s[k] >> j) & 1)) ++ c ; 
		a -= c, b -= c ; 
		if( (!a) && (!c) && (!b) ) continue ;  
		rep( k, 0, c ) {
			int f = 0, g = 0 ;
			if(k & 1) {
				rep(l, 0, a) if(!(l & 1)) f += C[a][l] ;
				rep(l, 0, b) if(!(l & 1)) g += C[b][l] ;
				r += C[c][k] * f * g ; 
			}
			else {
				rep(l, 0, a) if((l & 1)) f += C[a][l] ;
				rep(l, 0, b) if((l & 1)) g += C[b][l] ;
				r += C[c][k] * f * g ; 
			}
		} 
		Ans += d * r * (1ll << (cnt - a - b - c)) ; 
	} write() ; 
}
void Solve3() {
	rep( j, 1, cnt ) Ans |= s[j] ;
	cnt = 1 ; write() ; 
}
signed main()
{
	n = gi(), K = gi() ; int x ;
	rep( i, 1, n ) x = gi(), insert(x) ; 
	rep( i, 0, 64 ) if(tmp[i]) s[++ cnt] = tmp[i] ;
	C[0][0] = 1 ; 
	rep( i, 1, 64 ) rep( j, 0, 64 ) C[i][j] = (!j) ? 1 : (C[i - 1][j - 1] + C[i - 1][j]) ; 
	if( K >= 3 ) Solve1() ; 
	else if( K == 2 ) Solve2() ;
	else Solve3() ; 
	return 0 ;
}