码迷,mamicode.com
首页 > 其他好文 > 详细

【HDU 4305】Lightning(生成树计数)

时间:2016-08-13 06:35:00      阅读:249      评论:0      收藏:0      [点我收藏+]

标签:

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,这个过程因为我们是取模的,所以要用逆元,于是提前预处理逆元。

#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);
	}
}

  

【HDU 4305】Lightning(生成树计数)

标签:

原文地址:http://www.cnblogs.com/flipped/p/5767113.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!