這道題目是容斥原理的典型例題,主要體現了一種逆向思維。
如果我們試圖直接考慮在某種情況下,每一行和每一列都“不完全爲空”,會發現相對比較難以計算。此時可以反其道而行之,先把所有的情況算出來,然後再減去不合法的情況,會更好辦些,這裏就體現了容斥原理的思想。
使用容斥原理解決本題,大體上可以分爲兩種解決思路:
一、直接對整個棋盤進行容斥。
不考慮任何限制,每個格子就有黑和白兩種狀態,因此整個棋盤共有
接下來,我們考慮一個 3*3 的棋盤,不妨把第一行完全爲空的狀態集合記爲
注意到後面括號的部分很顯然就是直接套用容斥原理,只需解決如何計算若干集合的並集即可。
在本題中,如果一個
但是直接這樣分開枚舉
不難發現發現,對於都有
更具體一點,當
cdc 老師的 std:
#include <cstdio>
#include <cstring>
typedef long long LL;
#define Mod 1000000007
LL C[2050][2050], pow2[4000050];
int main()
{
int N, M;
scanf("%d%d", &N, &M);
C[0][0] = 1;
for (int i = 1; i <= N || i <= M; ++ i)
{
C[i][0] = 1;
for (int j = 1; j <= i; ++ j) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % Mod;
}
pow2[0] = 1;
for (int i = 1; i <= N*M; ++ i) pow2[i] = pow2[i-1] * 2 % Mod;
LL Ans = 0;
for (int i = 0; i <= N; ++ i)
for (int j = 0; j <= M; ++ j)
Ans = (Ans + (((i+j)&1)*(-2)+1) * pow2[(N-i)*(M-j)] % Mod * C[N][i] % Mod * C[M][j] % Mod) % Mod;
printf("%d\n", int( (Ans + Mod) % Mod ));
return 0;
}
二、只對行進行容斥。
現在假設我們先忽略對列的限制。
在一行中,要求不完全爲空,則要任意選
對於
接下來要做的就是再減去列的不合法情況。類似地,在每一行都不全爲白色的前提下,
雖然只需要枚舉
參考代碼(寫得這麼醜當然是我的啦):
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
#define sign(x) (x&1?-1:1)
const long long MOD = 1000000007LL;
long long C[3000][3000];
/*
long long C(int a, int b) {
if (a < b) return 0LL;
if (a - b < b) b = a - b;
long long ret = 1LL;
for (int i = a; i + b > a; i--) (ret *= i) %= MOD;
return ret;
}
*/
long long pow(int k, int p) {
if (k == 0) return 1LL;
else if (k == 1) return p;
else {
long long t = pow(k >> 1, p);
if (k & 1) return t * t % MOD * p % MOD; else return t * t % MOD;
}
}
long long g(int n, int m) {
return pow(n, pow(m, 2) - 1);
}
long long f(int n, int m) {
long long ret = g(n, m);
for (int i = 1; i <= m; i++) {
// printf("%lld %lld %lld\n", );
(ret += C[m][i] * g(n, m - i) % MOD * sign(i) + MOD) %= MOD;
}
return ret;
}
int main(void) {
freopen("2161.in", "r", stdin);
freopen("2161.out", "w", stdout);
int n, m; scanf("%d%d", &n, &m);
C[0][0] = 1;
for (int i = 1; i <= max(n, m); i++) {
C[i][0] = C[i][i] = 1LL;
for (int j = 1; j < i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
}
printf("%I64d\n", f(n, m));
return 0;
}