删边计数

这是一篇关于无向图的算法题目,要求在保持图连通的前提下,删除一定数量的边,并求解所有可能的删边方案总数。题目提供了一种利用状态压缩和生成树计数的方法来解决,通过考虑环的存在并处理环的两个方向,来避免计数重复。样例给出了不同规模的输入和输出示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间限制 : 1000 MS 空间限制 : 65536 KB

问题描述

给你一个由n个点m条边构成的无向图。要你从图中删除m-n条边,使得剩下的图示连通的。 问总共有多少种删边方案?

输入格式

第一行,两个整数n,m (1≤n≤16,n≤m≤n*(n−1)/2)
接下来m行,每行两个整数x和y,表示点x与点y之间有边相连。
图中没有自环,也没有重边。

输出格式

输出一个整数,表示答案,答案可能很大,mod 998244353再输出。

样例输入 1

4 5
1 2
2 3
3 4
4 1
1 3

样例输出 1

5

样例输入 2

10 18
2 1
3 2
4 1
4 2
4 3
5 2
6 1
6 3
7 3
7 5
8 1
8 5
8 6
9 6
9 8
10 1
10 5
10 6

样例输出 2

16334

样例输入 3

10 34
2 1
3 2
4 2
5 1
5 3
5 4
6 2
6 3
6 4
6 5
7 1
7 2
7 3
7 4
7 6
8 1
8 2
8 4
8 5
8 7
9 1
9 3
9 5
9 6
9 7
9 8
10 1
10 2
10 4
10 5
10 6
10 7
10 8
10 9

样例输出 3

38541686

题解

本题要求的即为原图生成树加上一条边的数量。如果直接算出所有生成树再加边上去的话显然会算重很多情况。我们把本题的特殊点——存在唯一一个环作为突破口。我们发现本题数据范围并不大,因此可用状态压缩统计出环的数量,设状态f[i][S]f[i][S]表示状态SS的路径以i为终点的数量,SS最低位的1为路径的起点(注意由于环可以两个方向遍历,最后答案要除以2)。处理完环后,把环缩点再进行生成树计数即可。

代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll a,b,n,m,ans,tot,id[20],G[20][20],map[20][20];
ll sum[1<<17],f[20][1<<17];
ll Gauss(ll n)
{
    ll temp=1,flag=0;
    for(ll i=1;i<=n;i++)
    {
        for(ll j=i+1;j<=n;j++)
            while(G[j][i])
            {
                ll t=G[i][i]/G[j][i];
                for(ll k=i;k<=n;k++) G[i][k]=(G[i][k]-t*G[j][k]%mod+mod)%mod;
                swap(G[i],G[j]),flag^=1;
            }
        temp=temp*G[i][i]%mod;
    }
    if(flag) temp=mod-temp;
    return (temp%mod+mod)%mod;
}
ll ksm(ll a,ll b)
{
    ll temp=1;
    while(b)
    {
        if(b&1) temp=temp*a%mod;
        b>>=1,a=a*a%mod;
    }
    return temp;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=m;i++) scanf("%lld%lld",&a,&b),map[a][b]++,map[b][a]++;
    for(ll S=1;S<(1<<n);S++)
    {
        ll cnt=__builtin_popcount(S),u=__builtin_ctz(S)+1;
        if(cnt==1) f[u][S]=1;
        for(ll v=u;v<=n;v++)
        {
            if(!f[v][S]) continue;
            for(ll i=u+1;i<=n;i++)
                if(!(S&(1<<(i-1)))&&map[i][v]) 
                    f[i][S|(1<<(i-1))]=(f[i][S|(1<<(i-1))]+f[v][S])%mod;
            if(cnt>=3&&map[u][v]) sum[S]=(sum[S]+f[v][S])%mod;
        }
    }
    for(ll S=1;S<(1<<n);S++)
    {
        if(!sum[S]) continue;
        memset(G,0,sizeof(G)),tot=1;
        for(ll i=1;i<=n;i++)
            if(S&(1<<(i-1))) id[i]=1;
            else id[i]=++tot;
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=n;j++)
                if(id[i]!=id[j]&&map[i][j]) G[id[i]][id[j]]--,G[id[i]][id[i]]++;
        ans=(ans+Gauss(tot-1)*sum[S]%mod+mod)%mod;
    }
    printf("%lld\n",ans*ksm(2,mod-2)%mod);
    return 0;
}
### 计数问题解决方案及其涉及的算法与数据结构 #### 一、计数问题概述 计数问题是计算科学领域中一类常见的问题,通常涉及到统计某些特定条件下的对象数量。这类问题的核心在于如何高效地遍历和统计满足条件的对象。为了优化性能,往往需要借助合适的 **数据结构** 和 **算法** 来解决问题。 --- #### 二、常用的数据结构支持计数操作 1. **哈希表 (Hash Table)** 哈希表是一种基于键值映射的数据结构,能够快速完成查找、插入和除操作。对于计数问题而言,可以利用哈希表来记录不同元素出现的次数。例如,在 Python 中可以通过 `dict` 或者 `collections.Counter` 实现这一功能[^3]。 ```python from collections import Counter data = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'] counter = Counter(data) print(counter) # 输出: Counter({'apple': 3, 'banana': 2, 'orange': 1}) ``` 2. **数组 (Array)** 如果待处理的数据范围较小且连续,则可以直接使用数组作为计数器。通过将数值映射到数组下标的方式,可以在 O(1) 时间内更新计数值[^1]。 ```python max_value = 10 counts = [0] * (max_value + 1) numbers = [2, 5, 2, 8, 5, 9] for num in numbers: if 0 <= num <= max_value: counts[num] += 1 print(counts) # 输出: [0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 1] ``` 3. **树形结构 (Tree Structure)** 对于动态区间查询或者大规模数据集上的频繁增改查场景,平衡二叉搜索树(如红黑树)或线段树可能是更好的选择。这些高级数据结构允许我们以较低的时间复杂度维护全局状态并执行局部调整[^2]。 --- #### 三、典型算法应对策略 1. **暴力枚举法** 这是最基础也是最容易理解的一种方法,适用于小型数据规模的情况。它通过对整个数据集合逐一扫描的方式来获取符合条件的结果数目。然而由于其较高的时间开销(O(n²)),并不适合实际应用中的大数据境。 2. **分治思想的应用** 当面对复杂的多维约束条件下求解最优解时,“分而治之”的理念显得尤为重要。比如经典的快速排序过程就体现了这种递归分解再组合的思想模式。同样地,在解决一些特殊的排列组合类别的计数难题上也可以采用类似的思路进行拆解分析。 3. **动态规划技术** 动态规划(Dynamic Programming),简称DP,主要用于寻找具有重叠子问题特性的最优化路径决策序列等问题解答方案之一。在很多情况下,如果一个问题能被划分为若干个更小相同类型的子问题,并且这些问题之间存在一定的依赖关系的话,那么就可以考虑运用此技巧提高运算效率降低冗余重复劳动成本。 --- #### 四、实例演示 假设我们需要在一个整型数组里找出所有两数相加等于给定目标值的配对总数: ```python def count_pairs(nums, target): seen = {} result = 0 for num in nums: complement = target - num if complement in seen: result += seen[complement] seen[num] = seen.get(num, 0) + 1 return result example_list = [1, 5, 7, -1, 5] target_sum = 6 print(f"Number of pairs with sum {target_sum}: {count_pairs(example_list, target_sum)}") # Output should be 3. ``` 上述代码片段展示了如何利用哈希表有效减少嵌套循带来的额外负担从而达到加速目的的同时保持清晰易懂的设计风格。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值