题目模型:给定平面上N个点。如果两点距离小于等于R,且两点间线段上没有其他点的时候,两点可以建立一条边。得到这个图后,求此图的生成树个数 mod 10007,如果图不连通则输出-1.
第一部分:构图。枚举两点是否符合距离限制,如果符合则对比此两点方向向量上是否有距离更小的其他点,然后根据情况建边删边。(O(N*N*log(N)))
第二部分:如果图连通,则根据生成树定理,从建好的图中构建M矩阵,高斯消元法求出行列式的值(由于是整数高消,需要提出系数,最后求其逆元)(O(N^3))
乘法逆元:(转自百度文库)
定义:
满足a*k≡1 (mod p)的k值就是a关于p的乘法逆元。
为什么要有乘法逆元呢?
当我们要求(a/b) mod p的值,且a很大,无法直接求得a/b的值时,我们就要用到乘法逆元。
我们可以通过求b关于p的乘法逆元k,将a乘上k再模p,即(a*k) mod p。其结果与(a/b) mod p等价。
证:(其实很简单。。。)
根据b*k≡1 (mod p)有b*k=p*x+1。
k=(p*x+1)/b。
把k代入(a*k) mod p,得:
(a*(p*x+1)/b) mod p
=((a*p*x)/b+a/b) mod p
=[((a*p*x)/b) mod p +(a/b)] mod p
=[(p*(a*x)/b) mod p +(a/b)] mod p
//p*[(a*x)/b] mod p=0
所以原式等于:(a/b) mod p
生成树的个数求法:
对于无向图G,它的Kirchhoff矩阵C定义为它的度数矩阵D减去它的邻接矩阵A。显然,这样的定义满足刚才描述的性质。
有了Kirchhoff矩阵这个工具,我们可以引入Matrix-Tree定理:
对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。
所谓n-1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。
第一部分:构图。枚举两点是否符合距离限制,如果符合则对比此两点方向向量上是否有距离更小的其他点,然后根据情况建边删边。(O(N*N*log(N)))
第二部分:如果图连通,则根据生成树定理,从建好的图中构建M矩阵,高斯消元法求出行列式的值(由于是整数高消,需要提出系数,最后求其逆元)(O(N^3))
乘法逆元:(转自百度文库)
定义:
满足a*k≡1 (mod p)的k值就是a关于p的乘法逆元。
为什么要有乘法逆元呢?
当我们要求(a/b) mod p的值,且a很大,无法直接求得a/b的值时,我们就要用到乘法逆元。
我们可以通过求b关于p的乘法逆元k,将a乘上k再模p,即(a*k) mod p。其结果与(a/b) mod p等价。
证:(其实很简单。。。)
根据b*k≡1 (mod p)有b*k=p*x+1。
k=(p*x+1)/b。
把k代入(a*k) mod p,得:
(a*(p*x+1)/b) mod p
=((a*p*x)/b+a/b) mod p
=[((a*p*x)/b) mod p +(a/b)] mod p
=[(p*(a*x)/b) mod p +(a/b)] mod p
//p*[(a*x)/b] mod p=0
所以原式等于:(a/b) mod p
生成树的个数求法:
对于无向图G,它的Kirchhoff矩阵C定义为它的度数矩阵D减去它的邻接矩阵A。显然,这样的定义满足刚才描述的性质。
有了Kirchhoff矩阵这个工具,我们可以引入Matrix-Tree定理:
对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。
所谓n-1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int M=366;
const int mod=10007;
struct Node
{
int x,y;
Node (){};
Node (int _x,int _y):x(_x),y(_y){};
Node operator -(Node pp){return Node(x-pp.x,y-pp.y);}
int operator ^(Node pp){return pp.x*x+pp.y*y;}
int operator *(Node pp){return x*pp.y-y*pp.x;}
void input()
{
scanf("%d%d",&x,&y);
}
int dist(Node pp)
{
return (x-pp.x)*(x-pp.x)+(y-pp.y)*(y-pp.y);
}
}p[M];
int degree[M][M];
int edge[M][M];
int C[M][M];
int exgcd(int a,int b,int &x,int &y)//乘法逆元返回的d是a,b的公约数,x是a mod b的逆元
{
if(b==0)
{
x=1;y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return d;
}
int det(int n)//计算n阶行列式的绝对值 % mod
{
int ans=1;
int flag=1;//行列交换的次数
int i,j,k;
for(i=0;i<n;i++)
{
if(C[i][i]==0)
{
for(j=i+1;j<n;j++)
if(C[j][i])break;
if(j==n)return 0;//某列的值全是0的ans=0;
flag=!flag;
for(int k=i;k<n;k++)
swap(C[i][k],C[j][k]);//i和j行交换
}
ans=ans*C[i][i]%mod;//对角线相乘
int x,y;
int tp=exgcd(C[i][i],mod,x,y);//x为逆元
for(k=i+1;k<n;k++)
C[i][k]=C[i][k]*x%mod;
for(int j=i+1;j<n;j++)
for(int k=i+1;k<n;k++)
{
C[j][k]=(C[j][k]-C[j][i]*C[i][k])%mod;
if(j==k)
C[j][k]=(C[j][k]+mod)%mod;
}
}
ans=(ans%mod+mod)%mod;
if(flag) return ans;
else return mod-ans;
}
int n,r;
int vist[M];
int cntp;
queue<int>que;
void bfs(int u)
{
vist[u]=1;
que.push(u);
cntp++;
while(!que.empty()){
int cur=que.front();
que.pop();
for(int i=0;i<n;i++){
if(edge[cur][i]&&!vist[i]){
vist[i]=1;
que.push(i);
cntp++;
}
}
}
}
int main()
{
int cas;
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d",&n,&r);
memset(degree,0,sizeof(degree));
memset(edge,0,sizeof(edge));
for(int i=0;i<n;i++)
p[i].input();
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
int d=p[i].dist(p[j]);
if(d<=r*r)
{
int flag=1;
for(int k=0;k<n;k++)
{
if(k==i||k==j)continue;
if((p[k]-p[j])*(p[k]-p[i])==0&&((p[i]-p[k])^(p[j]-p[k]))<0)//在同一直线,而且中间有点
{
flag=0;break;
}
}
if(flag)
degree[i][i]++,degree[j][j]++,edge[i][j]=edge[j][i]=1;
}
}
}
memset(vist,0,sizeof(vist));
cntp=0;
bfs(0);//判联通
if(cntp<n){
puts("-1");
continue;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
C[i][j]=degree[i][j]-edge[i][j];//
}
int ans=det(n-1);
cout<<ans<<endl;
}
return 0;
}