Eastest Magical Day Seep Group's Summer
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 308 Accepted Submission(s): 83
Problem Description
As we know, Tsuyuri Kumin likes sleeping in Eastest magical day sleep group's summer. But Rikka wants Kumin to play games with her. So she comes up with one problem:
Here is an undirected graphG![]()
with n![]()
vertices and m![]()
edges. Now you need to delete m−n![]()
edges and to make sure that the remain graph is connected. Rikka wants you to tell her the number of ways to choose the edges.
Kumin wants to go to sleep, so she asks you to answer this question. Can you help her?
Here is an undirected graph
Kumin wants to go to sleep, so she asks you to answer this question. Can you help her?
Input
There are at most 100 testcases。and there are no more 5 testcases withn≥10![]()
.
For each test case, the first line contains two integersn,m (1≤n≤16,n≤m≤n(n−1)
2![]()
)![]()
.
Thenm![]()
lines follows. Each of them contains two integers
u
i
,v
i![]()
![]()
,
meaning that there is an edge between u
i![]()
![]()
and v
i![]()
![]()
.
It is guaranteed that the graph doesn't contain self loops or multiple edges.
For each test case, the first line contains two integers
Then
Output
For each testcase print a single integer - the number of ways to choose the edges. The answer may be very large, so you only need to print the answer modulo 998244353.
Sample Input
4 5 1 2 2 3 3 4 4 1 1 3
Sample Output
5
Author
XJZX
Source
Recommend
-
Kirchhoff矩阵
*(1)G的度数矩阵D[G]是一个n*n的矩阵,而且满足:当i≠j时,dij=0;当i=j时,dij等于vi的度数; - *(2)G的邻接矩阵A[G]是一个n*n的矩阵,而且满足:假设vi,vj之间有边直接相连,则aij=1,否则为0;
- *定义图G的Kirchhoff矩阵C[G]为C[G]=D[G]-A[G];
- *Matrix-Tree定理:G的全部不同的生成树的个数等于其Kirchhoff矩阵C[G]不论什么一个n-1阶主子式的行列式的绝对值;
- *所谓n-1阶主子式,就是对于r(1≤r≤n),将C[G]的第r行,第r列同一时候去掉后得到的新矩阵,用Cr[G]表示;
- *
- *Kirchhoff矩阵的特殊性质:
- *(1)对于不论什么一个图G,它的Kirchhoff矩阵C的行列式总是0,这是由于C每行每列全部元素的和均为0;
- *(2)假设G是不连通的,则它的Kirchhoff矩阵C的任一个主子式的行列式均为0;
- *(3)假设G是一颗树,那么它的Kirchhoff矩阵C的任一个n-1阶主子式的行列式均为1;
- *
ACBANG_Magle的题解:
解题思路:
假设本题的要求只不过求这个图的生成树的个数。那么能够使用Kirchhoff矩阵求解:
可是该题中还存在一个问题,就是要求树上存在一个环,那么就须要缩点来实现了。将符合要求的环缩成一个点,最后的结果就是Σ(circle[i] * |C[G]|)。
先在仅剩下求环问题了。因为会缩点,因此,对相同的点的环。我们并不关心其环的形状,我们关心的不过环的数量,因此能够将点进行状压(点的个数唯独16个)。有例如以下定义:
dp[i][j] 代表 i 中状态下呈现链状的最后节点为j的种类
dp[i][j] = Σ dp[k][p](实际中是对dp[k][p]向后进行转移的)
同一时候保证,i状态下。链的起点为i中编号最小的点。
通过sum[i]数组记录i状态下的方案数。
通过上述步骤就能够解决该题了。
Tips:
因为缩点所产生的反复边是须要保存的。
因为环存在方向性。因此,就算规定了起点,对于一条链也会有2种转移方法。因此,最后结果须要除2。
在本题中与要注意使用逆元实现最后的除2,不能直接去除
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<functional> #include<iostream> #include<cmath> #include<cctype> #include<ctime> using namespace std; #define For(i,n) for(int i=1;i<=n;i++) #define Fork(i,k,n) for(int i=k;i<=n;i++) #define Rep(i,n) for(int i=0;i<n;i++) #define ForD(i,n) for(int i=n;i;i--) #define RepD(i,n) for(int i=n;i>=0;i--) #define Forp(x) for(int p=pre[x];p;p=next[p]) #define Forpiter(x) for(int &p=iter[x];p;p=next[p]) #define Lson (x<<1) #define Rson ((x<<1)+1) #define MEM(a) memset(a,0,sizeof(a)); #define MEMI(a) memset(a,127,sizeof(a)); #define MEMi(a) memset(a,128,sizeof(a)); #define INF (2139062143) #define F (998244353) #define eps (1e-3) #define MAXN (16+10) #define MAXM (16*16+10) typedef __int64 ll; ll mul(ll a,ll b){return (a*b)%F;} ll add(ll a,ll b){return (a+b)%F;} ll sub(ll a,ll b){return (a-b+llabs(a-b)/F*F+F)%F;} void upd(ll &a,ll b){a=(a%F+b%F)%F;} int n,m; ll r2; struct M { int n,m; ll a[MAXN][MAXN]; M(int _n=0){n=m=_n;MEM(a);} M(int _n,int _m){n=_n,m=_m;MEM(a);} void mem (int _n=0){n=m=_n;MEM(a);} void mem (int _n,int _m){n=_n,m=_m;MEM(a);} friend M operator*(M a,M b) { M c; For(k,a.m) For(i,a.n) For(j,b.m) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%F; return c; } void make_I(int _n) { n=m=_n; MEM(a) For(i,n) a[i][i]=1; } // 求行列式 long double mat[MAXN][MAXN],tmp[MAXN]; long double det() { For(i,n) For(j,m) mat[i][j]=a[i][j]; For(i,n) { int pos=i; while (fabs(mat[pos][i])<eps&&pos<n) ++pos; if (fabs(mat[pos][i])<eps) continue; if (pos^i) { copy(mat[pos]+1,mat[pos]+1+m+1,tmp+1); copy(mat[i]+1,mat[i]+1+m+1,mat[pos]+1); copy(tmp+1,tmp+1+m+1,mat[i]+1); } For(j,n) if (i^j) { long double p = mat[j][i]/mat[i][i]; For(k,m) mat[j][k]-=mat[i][k]*p; } } long double ans=1; For(i,n) ans*=mat[i][i]; return ans; } }A,D,C; ll dp[(1<<16)+100][MAXN],sum[(1<<16)+100]; const ll p2[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536}; int pos_1(ll x){ll cnt=1; while ((x&1)^1) cnt++,x>>=1; return cnt; } int all_1(ll x){ll cnt=0; while (x) cnt+=x&1,x>>=1; return cnt;} void solve(int n) { MEM(dp) MEM(sum) For(i,p2[n]-1) { ll cnt=all_1(i),pos=pos_1(i); if (cnt==1) { dp[i][pos]=1; } For(j,n) { if (i&p2[j-1]) { if (cnt>=3&&A.a[j][pos]) upd (sum[i], dp[i][j]); Fork(l,pos+1,n) if ( (p2[l-1]&i)==0 && A.a[j][l] ) upd(dp[i+p2[l-1]][l], dp[i][j] ); } } } } M a; int t[MAXN]; void work() { ll ans=0; For(i,p2[n]-1) { if (sum[i]) { ll cnt=all_1(i); ll p=0; For(j,n) if (i&p2[j-1]) t[j]=n-cnt+1; else t[j]=++p; a.mem(n-cnt); For(j,n) For(l,n) if (t[j]!=t[l]&&A.a[j][l]) { a.a[t[j]][t[j]]++; a.a[t[j]][t[l]]--; } ll t2=(ll)(fabs(a.det())+eps)%F; upd(ans,t2*sum[i]); } } ans=mul(ans,r2); cout<<ans<<endl; } int u[MAXM],v[MAXM]; __int64 pow2(ll x,ll b) { if (b==0) return 1; if (b==1) return x%F; ll p=pow2(x,b/2); p=p*p%F; if (b&1) p=p*x%F; return p; } int main() { // freopen("E.in","r",stdin); r2=pow2(2,F-2); while (cin>>n>>m) { A.mem(n),D.mem(n),C.mem(n); For(i,m) { scanf("%d%d",&u[i],&v[i]); D.a[u[i]][u[i]]++; D.a[v[i]][v[i]]++; A.a[u[i]][v[i]]++; A.a[v[i]][u[i]]++; } solve(n); work(); } return 0; }