題目
分析
把問題轉化爲“每個公司都要參與”的方案數。
假設現在只有 3 個公司,我們先算總方案數,減掉 1 公司不參與的方案數,減掉 2 公司不參與的方案數,減掉 3 公司不參與的方案數,這時候還不是答案,因爲我們把 1、2 公司都不參與的方案數,2、3 公司都不參與的方案數和 3、4 公司都不參與的方案數多減了一次(例如:減掉 1 公司不參與的方案數、減掉 2 公司不參與的方案數後,1、2 公司都不參與的方案數被減了兩次),所以要把它們加回來。
發現這是一個容斥。
也可以這樣理解:
是公司數, 代表 號公司不參與建設的情況,不妨設 爲偶數,根據容斥原理: 是總方案數,於是要求的就是 爆算即可,複雜度 。
錯因
- 沒做過幾道容斥的題,不太熟悉思路;
- 高斯消元又忘了換行的時候答案取相反數!
代碼
#include <bits/stdc++.h>
typedef std::pair<int, int> PII;
const int MAXN = 17;
const int MOD = 1000000007;
int N;
int M[MAXN + 5];
PII E[MAXN + 5][MAXN * MAXN + 5];
inline int Add(int x, const int &y) {
x += y; if (x >= MOD) x -= MOD; return x;
}
inline int Sub(int x, const int &y) {
x -= y; if (x < 0) x += MOD; return x;
}
inline int Mul(const int &x, const int &y) {
return (long long)x * y % MOD;
}
int Pow(int x, int y) {
int ret = 1;
while (y) {
if (y & 1)
ret = Mul(ret, x);
x = Mul(x, x);
y >>= 1;
}
return ret;
}
int Mat[MAXN + 5][MAXN + 5];
int Det(int n) {
n--;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
Mat[i][j] = Sub(Mat[i][j], 0);
int ret = 1;
for (int i = 1; i <= n; i++) {
int p = i;
for (int j = i + 1; j <= n; j++)
if (Mat[p][i] < Mat[j][i])
p = j;
if (p != i) {
std::swap(Mat[p], Mat[i]);
ret = Sub(0, ret);
}
if (!Mat[i][i])
return 0;
ret = Mul(ret, Mat[i][i]);
int inv = Pow(Mat[i][i], MOD - 2);
for (int j = i + 1; j <= n; j++) {
int tmp = Mul(Mat[j][i], inv);
for (int k = i; k <= n; k++)
Mat[j][k] = Sub(Mat[j][k], Mul(Mat[i][k], tmp));
}
}
return ret;
}
void AddEdge(int i) {
for (int j = 1; j <= M[i]; j++) {
int u = E[i][j].first, v = E[i][j].second;
Mat[u][u]++, Mat[v][v]++;
Mat[u][v]--, Mat[v][u]--;
}
}
int Tot[MAXN + 5];
int main() {
scanf("%d", &N);
for (int i = 1; i <= N - 1; i++) {
scanf("%d", &M[i]);
for (int j = 1; j <= M[i]; j++)
scanf("%d%d", &E[i][j].first, &E[i][j].second);
}
int lim = (1 << (N - 1)) - 1;
for (int S = 1; S <= lim; S++) {
int cnt = 0;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
Mat[i][j] = 0;
for (int i = 1; i <= N - 1; i++)
if (S & (1 << (i - 1))) {
cnt++;
AddEdge(i);
}
Tot[cnt] = Add(Tot[cnt], Det(N));
}
int Ans = 0;
for (int i = 1; i <= N - 1; i++)
Ans = ((N - i) & 1) ? Add(Ans, Tot[i]) : Sub(Ans, Tot[i]);
printf("%d", (Ans % MOD + MOD) % MOD);
return 0;
}