URAL2127 Determinant of a Graph 题解

这个题真的折磨了我超久的。全网几乎搜不到一个详细的题解,俺来写写吧。

题意:给你一个无自环无重边的连通无向图,求它邻接矩阵的行列式的值。

\(n\le 2*10^5,n-1\le m \le n+50\)

行列式定义,取一排列 \(p\),算它逆序对奇偶,再算 \(a_{i,p_i}\) 的乘积,乘起来,加起来是吧。经典的 \(i->p_i\) ,逆序对奇偶其实和“点数-环数”相等的,这个不难证。

发现这个 \(i->p_i\) 刚好能对应到图上。你不是要一堆 \(a_{i,p_i}\) 乘起来嘛,然后 \(a\) 又是原图的邻接矩阵。

所以一个排列 \(p\) 的权值不为 \(0\) ,应当是它连出来之后的若干个环都在图上出现,此处有特殊情况,就是“二元环”,退化成了一个边。

换句话,就是用若干环或边,覆盖所有点。

接下来考虑怎么利用 \(m\le n+50\) 以及上面的性质,对整个图进行一个改造。

用一些度数小的点入手,这样它们被覆盖的方法是很固定的。

Step1.度数为 \(1\) 的点

只能把这个点 \(u\) 和与它相连的点 \(v\) 覆盖了。也就是 \(p_u=v,p_v=u\)

然后可以把这俩点删了。

所以下面的讨论中,所有点度数大于等于2!

Step2. 度数为 \(2\) 的点

这一步就很 magic 了。

结论一:考虑四个相连的度数为 \(2\) 的点,将它们删去,并把两端的点连上,行列式值不变。

证明就是分类讨论。

设四个点分别为 \(u_1,u_2,u_3,u_4\)\(u_1\) 左的点为 \(l\)\(u_4\) 右的点为 \(r\)

有如下三种情况:

  1. 选择 \((u_1,u_2),(u_3,u_4)\) ,系数为正,在新图里,这等价于不选 \((l,r)\) 这个边。

  2. 选择 \((l,u_1),(u_2,u_3),(u_4,r)\) 系数为负,在新图里,这等价于选了 \((l,r)\) 这个边。

  3. 整体被吃进一个环里。在新图里还是被吃。

综上,新图与原图是等价的。

但这里有一些 corner case,就是 \(l=r\) 要尤其小心。我们发现,比如说原来有 \(5\) 个二度点,删成只剩 \(1\) 个,这里就连出重边了,这是不好的。

所以我们的方法是,对于一串二度点,一直 -=4,直到 \(\le 5\) 为止。

我们现在得到了一个新图。

结论二:此图点数是 \(O(m-n)\) 级别,此处 \(n,m\) 是原题给出的 \(n,m\)

首先,前面的那些操作有没有影响 \(m-n\) ?发现这个一定是变小了的。

假装我们把一串二度点删掉,把两端连上,形成一个度数全部 \(>2\) 的图。这个过程没有影响 \(m-n\)

设此时图点数 \(N\) ,边数 \(M\) ,由于度数之和大于等于 \(3N\)\(M\) 就大于等于 \(3N/2\) ,则 \(N/2\le M-N\le m-n\)

极限情况就是 \(N=2(m-n),M=3(m-n)\) 了。

再考虑二度点,相当于有 \(5M\) 个二度点。

所以我们的图极限情况下 \(17(m-n)\le 850\) 个点,而且卡不满。

事实上原题数据开 \(300\) 都是能过的,感觉可以证到一个更低的界?

直接对着这个图求行列式。这道题我们就做完了。

复杂度是 \(O(n\log n+C(m-n)^3)\) 其中 \(C=17^3\) 。为什么有 \(\log\) 呢,我为了方便删边用 set 存图(

代码实现时要注意的问题:

  1. 删一度点以 bfs 的方式来删,直接 dfs 会有神秘错误。

  2. 本人是从 \(>2\) 的度数的点开始找 \(2\) 度点。这样会忽略一种情况:整个图就是个环,需要特判

  3. 再说一遍:删二度点时删到 \(\le 5\) 为止。

#include<bits/stdc++.h>
using namespace std;
int n,m,d[201000];
bool vis[200100];
set<int>g[200100];
int ak;
const int mod=998244353;
queue<int>q;
void dk(int x){
	for(int v:g[x])if(!vis[v]){
		if(d[x]==1){puts("0");exit(0);}
		d[x]--;if(d[x]==1)q.push(x);
	}
}
void del(int x){
	if(vis[x])return;
	int u=-1;
	for(int v:g[x])if(!vis[v])u=v;
	if(u==-1){puts("0");exit(0);}
	vis[x]=vis[u]=1,ak=-ak;
	dk(x),dk(u);
}
bool vp[201000];
int id[201000];
int a[1010][1010];
int N;
void del(int &a,int b){
	(a-=b)%=mod;
	(a+=mod)%=mod;
}
vector<int>oo;
int main(){
	scanf("%d%d",&n,&m);
	ak=1;
	for(int i=0,u,v;i<m;i++)scanf("%d%d",&u,&v),g[u].insert(v),g[v].insert(u),d[u]++,d[v]++;
	for(int i=1;i<=n;i++)if(d[i]==1)q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		del(u);
	}
	bool fl=1;int ap=0;
	for(int i=1;i<=n;i++)if(!vis[i])fl&=(d[i]==2),ap++;
	if(fl){
		if(!ap)return printf("%d",(ak+mod)%mod),0;
		int su=-2;
		if(!(ap&1))su+=(((ap/2)&1)?-2:2);
		return printf("%d",(su*ak*((ap&1)?-1:1)%mod+mod)%mod),0; 
	}
	for(int i=1;i<=n;i++)if(!vis[i]&&d[i]>2){
		vector<int>nmd;
		for(int v:g[i])if(!vis[v]&&d[v]==2&&!vp[v])nmd.push_back(v);
		for(int v:nmd)if(!vp[v]){
			oo.clear();
			oo.push_back(v);
			int u=v,pr=i;
			int D;
			while(1){
				int nx;
				for(int op:g[u])if(pr!=op)nx=op;
				if(d[nx]==2)pr=u,u=nx,oo.push_back(u);
				else{D=nx;break;}
			}
			for(int nm:oo)vp[nm]=1;
			int sz=oo.size();
			if(sz>5){
				int O=sz%4;
				if(O<=1)O+=4;
				for(int ii=O;ii<sz;ii++)vis[oo[ii]]=1;
				g[oo[O-1]].erase(oo[O]);
				g[oo[O-1]].insert(D);
				g[D].erase(oo[sz-1]),g[D].insert(oo[O-1]);
			}
		}
	}
	for(int i=1;i<=n;i++)if(!vis[i])id[i]=++N;
	for(int i=1;i<=n;i++)if(id[i])for(int v:g[i])if(id[v])a[id[i]][id[v]]++;
	for(int i=1;i<=N;i++){
		for(int j=i+1;j<=N;j++){
			while(a[i][i]){
				int t=a[j][i]/a[i][i];
				for(int k=i;k<=N;k++)del(a[j][k],1ll*t*a[i][k]%mod);
				swap(a[i],a[j]);ak=-ak;
			}
			swap(a[i],a[j]),ak=-ak;
		}
	}
	for(int i=1;i<=N;i++)ak=1ll*ak*a[i][i]%mod;
	return printf("%d",(ak%mod+mod)%mod),0;
}
posted @ 2022-08-30 21:55  grass8woc  阅读(71)  评论(1编辑  收藏  举报