时间限制 : 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的路径以为终点的数量,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;
}