[Nowcoder 2018ACM多校第一场C] Fluorescent 2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/81388955

题目大意:
有n个开关, m个灯泡, 对于开关i, 它可以让一个灯泡子集 C i 中的灯泡的状态取反, 设初始时所有灯都是开着的。 求对于所有的开关集合S, f(S)表示对S中的所有开关都进行一次操作后, 亮着的灯泡数, 求 f ( S ) 3 ( 1 n 50 , 1 m 1000 , )

题目思路:
观察式子 f ( S ) 3
实际上是求 E ( ( X i ) 3 ) 2 n , (E表示期望, X i 表示第i盏灯亮)
即求 P ( X i X j X k ) , ( P )

考虑i, j, k同时亮的概率的求法
考虑i, j, k三列构成的n*3的01矩阵
我们用 Y i 表示第i行构成的行向量
即求在这 2 n 中行向量的选择中, 有多少组满足异或和为0的解
考虑求出该矩阵的行秩为R, 找到一个大小为R的极大线性无关行向量组, 则无论这R个以外的行向量如何选择, 都存在唯一的方法, 使用这R个中的子集, 使得最终异或和为0
所以对应的概率就是 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;
}

猜你喜欢

转载自blog.csdn.net/u013578420/article/details/81388955
今日推荐