【BZOJ3811/UOJ36】 玛里苟斯
Description
魔法之龙玛里苟斯最近在为加基森拍卖师的削弱而感到伤心,于是他想了一道数学题。
S 是一个可重集合,S={a1,a2,…,an}。
等概率随机取 S 的一个子集 A={ai1,…,aim}。
计算出 A 中所有元素异或 x, 求 xk 的期望。
Input
第一行两个正整数 n, k。
以下 n 行每行一个整数,表示 ai。
Output
如果结果是整数,直接输出。如果结果是小数(显然这个小数是有限的),输出精确值(末尾不加多余的 0)。
Sample Input
4 2
0
1
2
3
0
1
2
3
Sample Output
3.5
HINT
限制与约定
1≤n≤100000,1≤k≤5,ai≥0。最终答案小于 2^63 。k=1,2,3,4,5 各自占用 20% 的数据
Source
Solution
被学长安利去做这道题。。。线性基。
考虑一个性质:如果把集合内的一个数异或上另一个数,则这个集合的子集的异或和的集合不变。
什么叫做子集的异或和的集合?就是你从一个集合中选出一个子集,这个有2^n种选法,把选出来的数异或起来,然后这些异或起来的数组成的集合。
考虑证明:
假如我们一开始的集合是{a,b,...},我们把b异或上a,得到{a,a^b,...}。
然后原来和b有关但与a无关的子集的异或和,我们可以用a异或上a^b来代替;
原来和a和b都有关的子集的异或和,我们现在可以直接用a^b来代替。
其他的集合因为没有变,所以还是不变的。
那么总共产生的集合还是没有变。
既然我们有这么一个操作,那么我们就可以按位高斯消元了。我们把原集合高斯消元后得到的集合称作线性基(听别人说好像也叫秩?我对线代一无所知,所以不是很懂)。
可以证明线性基的个数是log权值的。
这题就是把式子展开完dfs一下。。。(你要是愿意每个子任务写k个for也是可以的)
Code
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 6 #ifdef WIN32 7 #define LL "%I64d" 8 #else 9 #define LL "%lld" 10 #endif 11 12 #ifdef CT 13 #define debug(...) printf(__VA_ARGS__) 14 #define setfile() 15 #else 16 #define debug(...) 17 #define filename "" 18 #define setfile() freopen(filename".in", "r", stdin); freopen(filename".out", "w", stdout) 19 #endif 20 21 #define R register 22 #define getc() (_S == _T && (_T = (_S = _B) + fread(_B, 1, 1 << 15, stdin), _S == _T) ? EOF : *_S++) 23 #define dmax(_a, _b) ((_a) > (_b) ? (_a) : (_b)) 24 #define dmin(_a, _b) ((_a) < (_b) ? (_a) : (_b)) 25 #define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0) 26 #define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0) 27 #define cabs(_x) ((_x) < 0 ? (- (_x)) : (_x)) 28 char _B[1 << 15], *_S = _B, *_T = _B; 29 typedef unsigned long long ull; 30 inline ull F() 31 { 32 R char ch; R ull cnt = 0; R bool minus = 0; 33 while (ch = getc(), (ch < '0' || ch > '9') && ch != '-') ; 34 ch == '-' ? minus = 1 : cnt = ch - '0'; 35 while (ch = getc(), ch >= '0' && ch <= '9') cnt = cnt * 10 + ch - '0'; 36 return minus ? -cnt : cnt; 37 } 38 typedef long long ll; 39 ull b[100], c[100]; 40 int top, m, sum, n, k, tmp, sn[110], maxlen; 41 ll ans; 42 bool flag; 43 void dfs(R int step, R ull state) 44 { 45 if (!step) 46 { 47 R int cnt = 0; 48 memset(c, 0, (m + 2) << 3); 49 for (R int i = 0; i <= m; ++i) 50 { 51 R ull x = b[i] & state; 52 for (; x; ) 53 { 54 tmp = __builtin_ctzll(x); 55 if (!c[tmp]) 56 { 57 c[tmp] = x; 58 break; 59 } 60 x ^= c[tmp]; 61 } 62 } 63 for (R int i = m; i >= 0; --i) 64 if (c[i]) 65 for (R int j = i - 1; j >= 0; --j) 66 if (c[j] & (1ull << i)) 67 c[j] ^= c[i]; 68 R ull summ = 0; 69 for (R int i = 0; i <= m; ++i) if (c[i]) ++cnt, summ ^= c[i]; 70 if (summ != state) return ; 71 // printf("%d %d\n", cnt, sum ); 72 if (sum < cnt) 73 { 74 ++sn[cnt - sum]; 75 cmax(maxlen, cnt - sum); 76 } 77 else ans += 1ull << (sum - cnt); 78 return ; 79 } 80 for (R int i = 0; i <= m; ++i) 81 { 82 sum += i; 83 dfs(step - 1, state | (1ull << i)); 84 sum -= i; 85 } 86 } 87 int main() 88 { 89 // setfile(); 90 n = F(), k = F(); 91 for (R int i = 1; i <= n; ++i) 92 { 93 R ull x = F(); 94 cmax(m, 63 - __builtin_clzll(x)); 95 for (; x; ) 96 { 97 tmp = __builtin_ctzll(x); 98 if (!b[tmp]) 99 { 100 b[tmp] = x; 101 break; 102 } 103 x ^= b[tmp]; 104 } 105 } 106 for (R int i = m; i >= 0; --i) 107 if (b[i]) 108 for (R int j = i - 1; j >= 0; --j) 109 if (b[j] & (1ull << i)) 110 b[j] ^= b[i]; 111 // for (R int i = 0; i <= m; ++i) printf("%llu ", b[i] ); 112 dfs(k, 0); 113 for (R int i = maxlen; i; --i) 114 { 115 sn[i - 1] += sn[i] >> 1; 116 sn[i] %= 2; 117 } 118 ans += sn[0]; 119 printf("%llu", ans ); 120 sn[1] ? puts(".5") : 0; 121 return 0; 122 }