CF40E Number Table 题解

Description

洛谷传送门

输入格式

第 1 行两个整数 (n)(m),表示一个 (n imes m) 的矩阵。

第 2 行一个整数 (k),表示有 (k) 个点已经被填上数。

(3) ~ (k + 2) 行,每行 3 个整数 (a)(b)(c),表示矩阵(M_{a,b} = c)

(k + 3) 行,1 个整数 (p),表示模数。

输出格式

一个整数,表示方案数。

Solution

我们首先考虑填一个空的矩阵有多少种方案数。

对于 1 ~ (n - 1) 行,我们先把最后一个数单独取出来一个数,剩下前 (m - 1) 个数随便填,最后一个数可以让这一行的乘积变成 -1。

同理,不管前 (n - 1) 行怎么填,最后一行总能让每一列的乘积变为 -1。

所以总方案数就是:

[ans = 2^{(n - 1)(m - 1)} ]

下面我们来考虑一下这道题。

首先可以判一波无解,先把结论放在这里:(n)(m) 奇偶性不同时,无解。

证明:

每一行,每一列乘积都为 -1,我们可以把这些 -1 乘在一起,即((-1)^{n + m}),再考虑一下这些数乘起来的本质是什么,是不是矩阵中每一个点的平方的乘积呢?是的。也就是说,乘积必为正数,就是 1。但当 (n)(m) 奇偶数性不同时,乘积是 -1,矛盾。

证毕.

我们继续来分析,观察到最后一句话:(0 leq k < max(n, m))

这有什么用呢?

我们假设 (n > m),上面的条件就变为了 (k < n),也就是说,不论你怎么填,必定有一行是空的(一个数都没有)。

我们发现,这样就有上面简化版题目的性质了,即,我们可以不考虑列了。

这时我们也可以套用上面的解法了,(O(n)) 枚举每一行,(ans = ans imes 2^{m - cnt_i - 1})(cnt_i) 是第 (i) 行 1 的个数,减去 1 是因为要空出来一个数来使这一行的乘积为 -1。

注意:这里也要判一下无解,当有一行全部被填满且乘积为 1 时,无解(显然)。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 1010;
int n, m, k, mod, flag;
int x[N], y[N], z[N];
int line[N], mul[N], fac[N];

int main(){
    scanf("%d%d%d", &n, &m, &k);
    if((n + m) & 1){
        puts("0");
        return 0;
    }
    if(n < m) swap(n, m), flag = 1;
    for(int i = 1; i <= n; ++i) mul[i] = 1;
    for(int i = 1, x, y, z; i <= k; ++i){
        scanf("%d%d%d", &x, &y, &z);
        if(flag) swap(x, y);
        line[x]++, mul[x] *= z;
    }
    scanf("%d", &mod);
    fac[0] = 1;
    for(int i = 1; i <= n; ++i)
        fac[i] = fac[i - 1] * 2 % mod;
    int ans = 1;
    for(int i = 1; i < n; ++i)
        if(!line[i]){
            swap(line[i], line[n]), swap(mul[i], mul[n]);
            break;
        }
    for(int i = 1; i < n && ans; ++i){
        if(line[i] == m && mul[i] == 1) ans = 0;
        if(line[i] < m) ans = ans * (long long)fac[m - line[i] - 1] % mod;
    }
    printf("%d
", ans);
    return 0;
}

End

本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15496844.html

原文地址:https://www.cnblogs.com/xixike/p/15496844.html