题目传送门
题目大意
给定一个$n imes m$的网格,每个格子上要么填$1$,要么填$-1$,有$k$个位置上的数是已经填好的,其他位置都是空的。问有多少种填法使得任意一行或一列上的数的乘积为$-1$.
$1 leqslant n, m leqslant 10^{3}$,$1 leqslant k < max (n, m)$。
$k$的范围醒目。那么意味着至少存在一行或者一列为空。
假设空的是一行。那么剩下的行只需要满足那一行的乘积为$-1$,而空的这一行对应一种唯一的填法。
可以计算出,空行补数后的乘积为$(-1)^{m} imes (-1)^{n - 1}$,即$(-1)^{m + n - 1}$。
所以特判$m. n$奇偶性不同的时候无解。然后就可以将每一行单独计算。
每一行中,要么只填奇数个$-1$,要么只填偶数个$-1$。这样就可以$O(nm)$的时间内解决这道题目。
但是这不能满足装逼爱好者的欲望。明明这东西可以做到O(n)。
定理1 当$n > 0$时,满足$sum_{k = 0}^{n}[2 mid k]C_{n}^{k} = sum_{k = 0} ^{n}[2 mid k]C_{n}^{k} = 2^{n - 1}$。
证明 当$n$为奇数时,根据式子$C_{n}^{k} = C_{n}^{n - k}$易证。
当$n$为偶数时,根据杨辉恒等式$C_{n}^{k} = C_{n - 1}^{k - 1} + C_{n - 1}^{k}$可得偶数位的和等于第$n - 1$层的和。
根据杨辉三角的性质,我们知道第$n - 1$层的和是$2^{n - 1}$,第$n$层的和是$2^{n}$。
所以第$n$层奇数位的和是$2^{n} - 2^{n - 1} = 2^{n - 1}$。
因此定理得证。
然后预处理2的幂,就可以做到$O(n)$了。
(另外提一句,即使没有 $k$ 那个限制,可以做到 $O(n + k)$)
Code
1 /** 2 * Codeforces 3 * Problem#40E 4 * Accepted 5 * Time: 60ms 6 * Memory: 2160k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int N = 1005; 13 14 int n, m, k, p; 15 boolean aflag; 16 int pow2[N]; 17 int cnt[N], pro[N]; 18 19 inline void init() { 20 scanf("%d%d", &n, &m); 21 scanf("%d", &k); 22 if (n < m) swap(n, m), aflag = true; 23 fill(pro + 1, pro + n + 1, 1); 24 for (int i = 1, u, v, x; i <= k; i++) { 25 scanf("%d%d%d", &u, &v, &x); 26 if (aflag) swap(u, v); 27 cnt[u]++, pro[u] *= x; 28 } 29 scanf("%d", &p); 30 } 31 32 inline void solve() { 33 if ((n & 1) != (m & 1)) { 34 puts("0"); 35 return; 36 } 37 38 pow2[0] = 1; 39 for (int i = 1; i <= n; i++) 40 pow2[i] = (pow2[i - 1] << 1) % p; 41 42 for (int i = 1; i < n; i++) 43 if (!cnt[i]) { 44 swap(cnt[i], cnt[n]); 45 swap(pro[i], pro[n]); 46 break; 47 } 48 49 int ans = 1; 50 for (int i = 1; i < n && ans; i++) { 51 if (cnt[i] == m) { 52 if (pro[i] == 1) 53 ans = 0; 54 } else 55 ans = ans * 1ll * pow2[m - cnt[i] - 1] % p; 56 } 57 printf("%d ", ans); 58 } 59 60 int main() { 61 init(); 62 solve(); 63 return 0; 64 }