题解[SHOI2016]黑暗前的幻想乡
矩阵树定理+容斥
题意
给定 \(n-1\) 个集合 \(S\),第 \(i\) 个集合里包含 \(m_i\) 条边,你需要从这 \(n-1\) 个集合里各选一条边,构成一棵树,问一共有多少种方案
题解
很明显题目所求的方案个数就是生成树个数,那么就可以用矩阵树定理来做
但题目又给了限制,即每个集合里只能选一条边,这个用矩阵树定理就没法做了
考虑容斥,令 \(f(A)\) 表示集合 \(A\) 中的边能够构成的生成树数量, \(g(i)\) 表示从集合 \(S\) 中任意 \(i\) 个不同集合的并集
那么答案就等于 \(\sum\limits_{i=0}^{n-1}(-1)^if(g(n-1-i))\)
说人话就是 \(n-1\) 个集合构成的无向图的生成树数量 \(- (n-2)\) 个集合构成的生成树数量 \(+(n-3)\) 个集合构成的生成树数量 ...
考虑 \(n\) 比较小,直接搜索就行
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define x first
#define y second
using namespace std;
const int MOD = 1e9 + 7;
typedef long long ll;
pair<int, int> edge[2505];
int head[21], net[2505], ver[2505], idx;
int n, a[21][21], tot;
bool is[21];
void add(int a, int b)
{
net[++idx] = head[a], ver[idx] = b, head[a] = idx;
}
int qmi(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1)
res = (ll)res * a % MOD;
a = (ll)a * a % MOD;
b >>= 1;
}
return res;
}
int work()
{
int res = 1, w = 1;
for (int i = 1; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (a[j][i] && !a[i][i])
{
swap(a[j], a[i]), w = -w;
break;
}
}
int inv = qmi(a[i][i], MOD - 2);
for (int j = i + 1; j < n; j++)
{
int temp = (ll)a[j][i] * inv % MOD;
for (int k = i; k < n; k++)
a[j][k] = (a[j][k] - (ll)a[i][k] * temp % MOD) % MOD;
}
}
for (int i = 1; i < n; i++)
res = (ll)res * a[i][i] % MOD;
res *= w;
return (res % MOD + MOD) % MOD;
}
int dfs(int ned, int last)
{
if (ned == 0)
{
memset(a, 0, sizeof(a));
int t = 0;
for (int i = 1; i < n; i++)
{
if (is[i])
continue;
t++;
for (int j = head[i]; j; j = net[j])
{
int v = ver[j];
int xx = edge[v].x, yy = edge[v].y;
a[xx][xx]++, a[yy][yy]++;
a[xx][yy]--, a[yy][xx]--;
}
}
return work();
}
int res = 0;
for (int i = last; i < n; i++)
{
if (is[i])
continue;
is[i] = true;
res = ((ll)res + dfs(ned - 1, i)) % MOD;
is[i] = false;
}
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
int m;
scanf("%d", &m);
for (int j = 1; j <= m; j++)
{
int u, v;
scanf("%d%d", &u, &v);
edge[++tot] = make_pair(u, v);
add(i, tot);
}
}
int ans = 0, w = 1;
for (int i = 0; i < n; i++)
ans = (ans + dfs(i, 1) * w) % MOD, w = -w;
printf("%d", (ans % MOD + MOD) % MOD);
return 0;
}