标签:
给出n个点的坐标,距离不超过r的点如果中间没有其它点则可以连一条边,最后求生成树的数量,对10007取模。
Matrix-Tree定理:Kirchhoff矩阵任意n-1阶子矩阵的行列式的绝对值就是无向图的生成树的数量。
Kirchhoff矩阵的定义是度数矩阵-邻接矩阵。
1、G的度数矩阵D[G]:n*n的矩阵,Dii等于Vi的度数,其余为0。
2、G的邻接矩阵A[G]:n*n的矩阵, Vi、Vj之间有边直接相连,则
Aij=1,否则为0。
有了这个定理,我们要只要构造Kirchhoff矩阵,然后计算行列式就行了,注意要取模。
构造矩阵需要判断一下满足距离不超过r的两点之间是否有其它点,直接三层循环用叉积判断ijk是否共线,如果共线k是否在ij之间。
然后是行列式的计算:
是线性代数的知识,先通过初等变换化成 上三角的行列式,主对角线的积就是行列式的值了。
而初等变换的过程,如果是交换两行,行列式要乘上-1,所以记录一下交换了几次,最后根据奇偶来乘-1。我们要用Cii来消去它下面的数。第i行每个数都要除以Cii,这个过程因为我们是取模的,所以要用逆元,于是提前预处理逆元。
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define sqr(x) ((x)*(x)) using namespace std; const int M=10007; const int N=301; int inv[M],mat[N][N]; void init(){//求逆元 inv[1]=1; for(int i=2;i<M;i++) inv[i]=(M-M/i)*inv[M%i]%M; } int det(int c[][N],int n){//求矩阵c的n阶顺序主子式 int i,j,k,w=0,ans=1; for(i=0;i<n;i++) for(j=0;j<n;j++) c[i][j]=(c[i][j]%M+M)%M; for(i=0;i<n;i++){ for(j=i;j<n;j++)//找出第i行起第i列不为0的行 if(c[i][j])break; if(i!=j){ w++;//交换次数 swap(c[i],c[j]); } ans=ans*c[i][i]%M; for(j=i+1;j<n;j++)//第j行第i列变为0 for(k=n;k>i;k--)//该行每列减去第i列的值*d c[j][k]=(c[j][k]-c[i][k]*inv[c[i][i]]%M*c[j][i]%M+M)%M; } return (ans*(w&1?-1:1)+M)%M; } struct point{ int x,y; }p[N]; int same(point a,point b,point c){//判断是否共线 return (a.x-c.x)*(b.y-c.y)==(b.x-c.x)*(a.y-c.y) &&min(a.x,c.x)<=b.x&&max(a.x,c.x)>=b.x &&min(a.y,c.y)<=b.y&&max(a.y,c.y)>=b.y; } int main(){ init(); int t,n,r; scanf("%d",&t); while(t--){ memset(mat,0,sizeof mat); scanf("%d%d",&n,&r); for(int i=0;i<n;i++) scanf("%d%d",&p[i].x,&p[i].y); for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) if(sqrt(sqr(p[i].x-p[j].x)+sqr(p[i].y-p[j].y))<=r){//距离不大于r int ok=1; for(int k=0;k<n;k++) if(k!=i&&k!=j&&same(p[i],p[k],p[j])) ok=0; if(ok){//构造Kirchhoff矩阵 mat[i][j]=mat[j][i]=-1; mat[i][i]++;mat[j][j]++; } } int ans=det(mat,n-1); printf("%d\n",ans?ans:-1); } }
标签:
原文地址:http://www.cnblogs.com/flipped/p/5767113.html