把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF53E Dead Ends 分析

题目概述

给一个含有 \(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;
}

可以用矩阵树来写。

posted @ 2025-11-07 11:40  high_skyy  阅读(3)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end