HYSBZ 4596 黑暗前的幻想乡
题目描述:
有 N 个点和 M 个集合,每个集合包含若干条边,现在需要选择一些集合,再从这些集合里选出一些边,构造一棵生成树,问这样做的方案数(两种方案不同当且仅当选择的集合不同,或者一个集合中选择的边不同)。
题解:
首先容斥,求选择某些集合的答案,这个可以用矩阵树定理做。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 20
const long long MOD = ((long long)(1e9 + 7));
static int N, node[MAXN * MAXN * MAXN][2], start[MAXN], len[MAXN];
static long long ans, mtx[MAXN][MAXN];
inline long long det(long long A[MAXN][MAXN], int N)
{
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
mtx[i][j] += MOD, mtx[i][j] %= MOD;
long long ans = 1, div = 1;
for (int i = 1; i <= N; i++)
{
for (int j = i + 1; j <= N; j++)
{
long long x = A[i][i], y = A[j][i];
while (y)
{
long long t = x / y; x %= y; swap(x, y);
for (int k = i; k <= N; k++)
A[i][k] -= t * A[j][k] % MOD, A[i][k] += MOD, A[i][k] %= MOD;
for (int k = i; k <= N; k++) swap(A[i][k], A[j][k]);
div = -div;
}
}
if (!(ans = ans * A[i][i] % MOD)) return 0;
}
return (div * ans + MOD) % MOD;
}
int main()
{
scanf("%d", &N);
for (int i = 0; i < N - 1; i++)
{
scanf("%d", &len[i]);
start[i] = i ? start[i-1] + len[i-1] : 0;
for (int j = start[i]; j < start[i] + len[i]; j++)
scanf("%d%d", &node[j][0], &node[j][1]);
}
for (int i = 0; i < (1 << (N - 1)); i++)
{
int tot = 0;
memset(mtx, 0, sizeof(mtx));
for (int j = 0; j < N - 1; j++)
if ((i >> j) & 1)
{
tot++;
for (int k = start[j]; k < start[j] + len[j]; k++)
{
mtx[node[k][0]][node[k][1]]--; mtx[node[k][1]][node[k][0]]--;
mtx[node[k][0]][node[k][0]]++; mtx[node[k][1]][node[k][1]]++;
}
}
ans = (ans + (((N - tot) & 1) ? 1LL : -1LL) * det(mtx, N - 1)) % MOD;
}
printf("%lld\n", (ans % MOD + MOD) % MOD);
}
提交记录(AC / Total = 1 / 4):
Run ID | Remote Run ID | Time(ms) | Memory(kb) | Result | Submit Time |
---|---|---|---|---|---|
8532031 | 1942474 | 8 | 820 | RE | 2017-03-23 15:21:47 |
8532050 | 1942481 | 136 | 1132 | RE | 2017-03-23 15:24:11 |
8532082 | 1942491 | 136 | 1132 | RE | 2017-03-23 15:27:17 |
8532444 | 1942624 | 11316 | 888 | AC | 2017-03-23 16:05:54 |