行列式
题目大意
给定一个无向联通图,求其邻接矩阵行列式。
\(n\leq 3\times 10^4 , m\leq 3\times 10^5\) ,保证每个边双联通分量点数 \(\leq 50\)
题解
考虑这个邻接矩阵的行列式的意义,在不考虑行列式中的 \(-1\) 系数的情况下,就是选出若干个存在的有向环,不重不漏地覆盖每个点的方案数。
我们发现一个排列中环与环之间产生的逆序对数是可以忽略的,因此如果每个环都在一个边双内部,答案就是每个边双的答案的乘积。
考虑一个桥出现在了环中,那么一定是桥的两个端点组成了二元环。
考虑建出边双树, \(F_{x,0/1}\) 表示 \(x\) 号边双,其与边双树父亲连接的那个点有没有被环覆盖的方案数。
若 \(x\) 中一个点 \(u\) 与 \(x\) 的某个儿子 \(y\) 中的一个点 \(v\) 组成了二元环,那么我们可视为在 \(x\) 内部的邻接矩阵中,\(u\) 自己添加了一个自环,其权值自带 \(-1\) 的系数,然后求行列式即可算出 \(F_{x,1}\)
同样,我们将 \(x\) 与 \(x\) 父亲连接的点 \(w\) 在邻接矩阵的边都扣抠掉,并加上一个自环即可等价算出 \(F_{x,0}\)
#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<" = "<<x
#define sp <<" "
#define el <<endl
#define fgx cerr<<"-----------------------------------"<<endl
#define LL long long
#define uint unsigned int
#define ULL unsigned long long
#define LDB long double
#define DB double
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
using namespace std;
inline int read(){
int nm=0; bool fh=true; char cw=getchar();
for(;!isdigit(cw);cw=getchar()) fh^=(cw=='-');
for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
return fh?nm:-nm;
}
#define mod 998244353
namespace CALC{
inline int add(int x,int y){return (x+y>=mod)?(x+y-mod):(x+y);}
inline int mns(int x,int y){return (x-y<0)?(x-y+mod):(x-y);}
inline int mul(int x,int y){return (LL)x*(LL)y%mod;}
inline void upd(int &x,int y){x=((x+y>=mod)?(x+y-mod):(x+y));}
inline void dec(int &x,int y){x=((x-y<0)?(x-y+mod):(x-y));}
inline int qpow(int x,int sq){int res=1;for(;sq;sq>>=1,x=mul(x,x))if(sq&1)res=mul(res,x);return res;}
}using namespace CALC;
#define M 30020
int n,m,t[53][53],p[53][53];
inline int calc(int K,int ans=1){
if(!K) return 1; memcpy(p,t,sizeof(p));
for(int k=1,i=1;i<=K;i++){
for(k=i;k<=K&&!p[k][i];++k); if(k>K) return 0;
if(k^i){for(int j=i;j<=K;j++) swap(p[k][j],p[i][j]);ans=mul(ans,mod-1);}
int bas=p[i][i]; ans=mul(ans,bas),bas=qpow(bas,mod-2);
for(int j=i;j<=K;j++) p[i][j]=mul(p[i][j],bas);
for(int w=i+1;w<=K;w++) if(p[w][i]){
int tmp=p[w][i];
for(int j=i;j<=K;j++) dec(p[w][j],mul(tmp,p[i][j]));
}
} return ans;
}
int fs[M],nt[M*20],to[M*20],u[M*10],v[M*10],tmp,id[M];
inline void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;}
int dfn[M],low[M],cnt,be[M],F[M][2],anc[M],tot,G[M],S[M],top;
vector<int>nd[M],eg[M],son[M];
inline void init(int x,int last){
dfn[x]=++cnt,low[x]=cnt,S[++top]=x;
for(int i=fs[x];i!=-1;i=nt[i]) if(to[i]^last){
if(!dfn[to[i]]) init(to[i],x);
low[x]=min(low[x],low[to[i]]);
} if(low[x]<dfn[x]) return; ++tot;
while(!be[x]) be[S[top]]=tot,nd[tot].pb(S[top]),top--;
if(last) son[last].pb(x); anc[tot]=x;
}
inline void DP(int w){
for(int i=0,TP=nd[w].size();i<TP;i++)
for(int x=nd[w][i],j=0,SZ=son[x].size();j<SZ;++j) DP(be[son[x][j]]);
int K=nd[w].size(); memset(t,0,sizeof(t)); sort(nd[w].begin(),nd[w].end());
for(int j=0;j<K;j++){
int y,x=nd[w][j],g1=0,g0=1; id[x]=j+1;
for(int i=0,TP=son[x].size();i<TP;++i){
y=be[son[x][i]],g1=mul(g1,F[y][1]);
dec(g1,mul(g0,F[y][0])),g0=mul(g0,F[y][1]);
} t[j+1][j+1]=g1,G[j+1]=g0;
}
for(int i=0,TP=eg[w].size();i<TP;i++){
int x=id[eg[w][i]/M],y=id[eg[w][i]%M];
t[x][y]=G[x],t[y][x]=G[y];
} F[w][1]=calc(K); int ps=id[anc[w]];
for(int i=1;i<=K;i++) t[i][ps]=t[ps][i]=0;
for(int i=1;i<K;i++) for(int j=1;j<K;j++){
if(i<ps&&j<ps) continue;
if(i<ps) t[i][j]=t[i][j+1];
else if(j<ps) t[i][j]=t[i+1][j];
else t[i][j]=t[i+1][j+1];
} F[w][0]=calc(K-1);
for(int x=anc[w],i=0,TP=son[x].size();i<TP;++i)
F[w][0]=mul(F[w][0],F[be[son[x][i]]][1]);
}
int main(){
n=read(),m=read(),read(),memset(fs,-1,sizeof(fs));
for(int i=1,x,y;i<=m;i++) x=read(),y=read(),u[i]=x,v[i]=y,link(x,y),link(y,x); init(1,0);
for(int i=1;i<=m;i++) if(be[u[i]]==be[v[i]]) eg[be[u[i]]].pb(u[i]*M+v[i]); DP(tot);
printf("%d\n",F[tot][1]); return 0;
}