BZOJ5297 CQOI2018 社交网络

考前挣扎

有向图生成树

我们知道对于无向图生成树可以通过矩阵树定理来算具体就是

度数矩阵 - 邻接矩阵 去掉任意一行一列得到余子式 对行列式求值

有向图生成树分为外向和内向两种

外向生成树当然是所有边指向儿子 他的矩阵树定理是这个样子

对于有向边(x,y)度数矩阵d[y][y] ++ 邻接矩阵e[x][y] ++

内向生成树所有边指向父亲

对于有向边(x,y)度数矩阵d[x][x] ++ 邻接矩阵e[x][y] ++

(说白了就是外向生成树反过来)

记忆的一个方法就是度数矩阵只记录入边(就是生成树的入边 【哪里不会点哪里

然后照常进行度数-邻接 然后我们由于是有向边 那么我们去掉的一行一列(i,i)就表示以i为根

然后正常行列式求值即可

(这个题小坑点 边是反着读的= =

//Love and Freedom.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mdn 10007
#define N 300
using namespace std;

int a[N][N],n,m;
int inv[mdn];
int gauss()
{
	int ans = 1;
	for(int i=1,j;i<=n;i++)
	{
		if(!a[i][i])
		{
			for(j=i+1;j<=n;j++)
				if(a[j][i])	break;
			if(j>n)	return 0;
			swap(a[j],a[i]);
			ans = -ans;
		}
		int iv = inv[a[i][i]];
		for(int j=i+1;j<=n;j++)
		{
			int w = iv*a[j][i]% mdn;
			for(int k=i;k<=n;k++)
				a[j][k] = (a[j][k]-w*a[i][k]%mdn +mdn)%mdn;
		}
	}
	
	for(int i=1;i<=n;i++)
		ans = ans*a[i][i]%mdn;
	return ans<0?ans+mdn:ans;
}

int main()
{
	int x,y; inv[1] = 1;
	for(int i=2;i<mdn;i++)
		inv[i] = (mdn-mdn/i)*inv[mdn%i]%mdn;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		x--; y--;
		a[x][x]++; a[y][x]--;
	}
	n--; printf("%d
",gauss());
	return 0;
}
原文地址:https://www.cnblogs.com/hanyuweining/p/10321869.html