Codeforces 40E Number Table - 组合数学

题目传送门

  传送门I

  传送门II

题目大意

  给定一个$n\times m$的网格,每个格子上要么填$1$,要么填$-1$,有$k$个位置上的数是已经填好的,其他位置都是空的。问有多少种填法使得任意一行或一列上的数的乘积为$-1$.

  $1 \leqslant n, m \leqslant 10^{3}$,$1 \leqslant k < \max (n, m)$。

  $k$的范围醒目。那么意味着至少存在一行或者一列为空。

  假设空的是一行。那么剩下的行只需要满足那一行的乘积为$-1$,而空的这一行对应一种唯一的填法。

  可以计算出,空行补数后的乘积为$(-1)^{m}\times (-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 \nmid 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\n", ans);
58 }
59 
60 int main() {
61     init();
62     solve();
63     return 0;
64 }
posted @ 2018-03-08 08:16  阿波罗2003  阅读(356)  评论(0编辑  收藏  举报