题目
分析
把问题转化为“每个公司都要参与”的方案数。
假设现在只有 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;
}