Problem Description
There are N robots standing on the ground (Don't know why. Don't know how).
Suddenly the sky turns into gray, and lightning storm comes! Unfortunately, one of the robots is stuck by the lightning!
So it becomes overladen. Once a robot becomes overladen, it will spread lightning to the near one.
The spreading happens when: Robot A is overladen but robot B not. The Distance between robot A and robot B is no longer than R. No other robots stand in a line between them. In this condition, robot B becomes overladen. We assume that no two spreading happens at a same time and no two robots stand at a same position.
The problem is: How many kind of lightning shape if all robots is overladen? The answer can be very large so we output the answer modulo 10007. If some of the robots cannot be overladen, just output -1.
Input
There are several cases. The first line is an integer T (T < = 20), indicate the test cases. For each case, the first line contains integer N ( 1 < = N < = 300 ) and R ( 0 < = R < = 20000 ), indicate there stand N robots; following N lines, each contains two integers ( x, y ) ( -10000 < = x, y < = 10000 ), indicate the position of the robot.
Output
One line for each case contains the answer.
Sample Input
3
3 2
-1 0
0 1
1 0
3 2
-1 0
0 0
1 0
3 1
-1 0
0 1
1 0
Sample Output
3
1
-1
题意:
给出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,这个过程因为我们是取模的,所以要用逆元,于是提前预处理逆元。
(对于这题我有一个疑惑,如果行列式的值是负数,那么生成树的个数就是它的绝对值,答案不应该是这个绝对值再取模吗,但是它的数据却是MOD-绝对值。比如行列式是-3,生成树应该是3,题目的正确答案却是10004)
代码语言:javascript复制#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)
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;
}
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("%dn",ans?ans:-1);
}
}
另外一种求行列式的方法可以不用求逆元,万一mod是非质数就不能用求逆元了,所以这种方法就派上用场了。
代码语言:javascript复制#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 mat[N][N];
int det(int c[][N],int n){
int i,j,k,t,ret=1;
for(i=0;i<n;i )
for(j=0;j<n;j ) c[i][j]%=M;
for(i=0; i<n; i ){
for(j=i 1; j<n; j )
while(c[j][i])
{
t=c[i][i]/c[j][i];//类似辗转相除
for(k=i; k<n; k )
c[i][k]=(c[i][k]-c[j][k]*t)%M;
swap(c[i],c[j]);
ret=-ret;
}
if(c[i][i]==0)
return 0L;
ret=ret*c[i][i]%M;
}
return (ret 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(){
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){
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){
mat[i][j]=mat[j][i]=-1;
mat[i][i] ;mat[j][j] ;
}
}
int ans=det(mat,n-1);
printf("%dn",ans?ans:-1);
}
}