题目大意:
有n个开关, m个灯泡, 对于开关i, 它可以让一个灯泡子集
Ci
C
i
中的灯泡的状态取反, 设初始时所有灯都是开着的。 求对于所有的开关集合S, f(S)表示对S中的所有开关都进行一次操作后, 亮着的灯泡数, 求
∑f(S)3
∑
f
(
S
)
3
。
(1≤n≤50,1≤m≤1000,)
(
1
≤
n
≤
50
,
1
≤
m
≤
1000
,
)
题目思路:
观察式子
∑f(S)3
∑
f
(
S
)
3
实际上是求
E((∑Xi)3)∗2n,
E
(
(
∑
X
i
)
3
)
∗
2
n
,
(E表示期望,
Xi
X
i
表示第i盏灯亮)
即求
∑P(XiXjXk),
∑
P
(
X
i
X
j
X
k
)
,
(P表示概率)
(
P
表
示
概
率
)
考虑i, j, k同时亮的概率的求法
考虑i, j, k三列构成的n*3的01矩阵
我们用
Yi
Y
i
表示第i行构成的行向量
即求在这
2n
2
n
中行向量的选择中, 有多少组满足异或和为0的解
考虑求出该矩阵的行秩为R, 找到一个大小为R的极大线性无关行向量组, 则无论这R个以外的行向量如何选择, 都存在唯一的方法, 使用这R个中的子集, 使得最终异或和为0
所以对应的概率就是
12R
1
2
R
由于对于矩阵行秩=列秩, 我们转而去求列秩
由于这是一个只有3列的矩阵, 出了全0矩阵秩为0的情况外, 只有1,2,3三种可能。
考虑枚举某一列i, 去消所有的列, 对于列j, k
如果j, k相同则秩为2
如果j, k不同则秩为3
在考虑特殊情况下, i,j,k中有多少个0向量则相应的秩也要减去多少。
可以再消元后用map将所有不同值的列hash起来, 采用计数的方法一起算。
Code:
#include <map>
#include <set>
#include <map>
#include <bitset>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define db double
#define fi first
#define se second
#define mp(x, y) make_pair(x, y)
#define ls (x << 1)
#define rs ((x << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
const int N = 55;
const int M = 1010;
const int mo = (int)1e9 + 7;
int n, m, C[N][M]; ll ans, X[M], pw[N];
int main(){
pw[0] = 1;
for (int i = 1; i < N; i ++) pw[i] = pw[i - 1] * 2 % mo;
while (scanf("%d %d", &n, &m) != EOF){
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
scanf("%1d", C[i] + j);
for (int i = 1; i <= m; i ++)
for (int j = 1; j <= n; j ++)
X[i] |= (C[j][i] << (j - 1));
for (int i = 1; i <= m; i ++){
map<ll, int > mm;
map<ll, int > :: iterator it;
int pos = -1;
for (int j = 0; j < n; j ++)
if ((X[i] >> j) & 1) {pos = j; break;}
for (int j = 1; j <= m; j ++)
if (pos != -1 && ((X[j] >> pos) & 1)) mm[X[i] ^ X[j]] ++;
else mm[X[j]] ++;
for (it = mm.begin(); it != mm.end(); it ++){
(ans += 1ll * it->se * it->se * pw[n - 2 + (it->fi == 0) + (X[i] == 0)] % mo) %= mo;
if (it->fi == 0)
(ans += 1ll * it->se * (m - it->se) * pw[n - 2 + (X[i] == 0)] % mo) %= mo;
else{
(ans += 1ll * it->se * (m - it->se - mm[0]) * pw[n - 3 + (X[i] == 0)] % mo) %= mo;
(ans += 1ll * it->se * mm[0] * pw[n - 2 + (X[i] == 0)] % mo) %= mo;
}
}
}
printf("%lld\n", ans);
ans = 0;
for (int i = 1; i <= m; i ++) X[i] = 0;
}
return 0;
}