行列式
题目大意
给定一个无向联通图,求其邻接矩阵行列式。
\(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; }