题目概述
给一个含有 \(n\) 个点和 \(m\) 条边的无向连通图,求恰好有 \(d\) 个叶子的生成树的个数。
数据范围:\(1\leq d\leq n\leq 10,m\leq \frac{n(n-1)}{2}\)。
分析
注意到 \(n\leq 10\),我们可能会有 \(2^n\) 或者 \(3^n\) 做法。
考虑生成树的连通性,肯定有 \(s1\) 表示多少个点与 \(1\) 连通。
叶子的情况也来一个 \(s2\)。
那么显然:\(s2\subseteq s1\),我们枚举子集是 \(\mathcal{O}(3^n)\) 的。
考虑转移就从中选一个没有与 \(1\) 连通的点进行转移即可。
这里是 \(\mathcal{O}(n^2)\) 的。
但是可能算重,那么我们钦定加入一个点之后如果它成为了叶子那么得是最大的,这样肯定不会算重。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stdlib.h>
#include <cstring>
#include <vector>
#define int long long
#define N 15
#define M (1 << 10) + 5
using namespace std;
int n,m,d;
int f[M][M],ans;
bool g[N][N];
signed main(){cin >> n >> m >> d;for (int i = 1;i <= m;i ++) {int u,v;scanf("%lld%lld",&u,&v);g[u][v] = g[v][u] = 1;}for (int i = 2;i <= n;i ++)if (g[1][i]) f[1 << i - 1 | 1][1 << i - 1 | 1] = 1;int t = (1 << n);for (int i = 0;i < t;i ++)for (int j = i;j;j = i & (j - 1)) {if (!f[i][j]) continue;int cnt1 = 0,cnt2 = 0;for (int k = 1;k <= n;k ++) {bool p1 = (i >> k - 1) & 1,p2 = (j >> k - 1) & 1;if (p2) cnt2 ++;else if (p1) cnt1 ++;else continue;for (int l = 1;l <= n;l ++)if (((i >> l - 1) & 1) == 0 && g[k][l]) {int to;if (p2) to = (j ^ (1 << l - 1) ^ (1 << k - 1));else to = (j ^ (1 << l - 1));if ((to >> l - 1) == 1) f[i | (1 << l - 1)][to] += f[i][j];}}if (cnt2 == d && cnt1 + cnt2 == n) ans += f[i][j];}cout << ans;return 0;
}
可以用矩阵树来写。