HDOJ 4305 - Lightning 判断点在线段上+构造Matrix Tree求生成树的个数+高斯消元

             题意:

                       有N个点在二维平面上...两点间若距离不大于R并且构成的线段中没有其他的点..则可以连一条边..现在问..这个图有多少个生成树

             题解:

                       觉得这题好屌...一些计算几的知识+神奇的矩阵+高斯消元(同时要求逆元)...

                       先构边...按照题目的要求..ok的就有边...构边就直接构造Matrix Tree吧...而Matrix Tree构造的矩阵意思是: 对角线上为每个点的度..非对角线上为两点间连着边的个数的相反数..由于本题没有重边.那么就是有边则为-1..没边则为0...构造出矩阵..去掉任意一个十字..剩下的行列式之合就是该图的生成树的个数..好神奇..不懂如何证明...直接拿来用了...最后就是要求一个矩阵的行列式...方法有很多种...一般都是用高斯消元...高斯消元的方法是每次让一列只剩下一个数..其他数约掉...最后矩阵转化为一个上三角..对角线乘积则为矩阵的行列式...手算的时候一般会出现几分之几...程序如果也用double来写最后又回到int..可能会产生一些精度问题..但高斯消元是可以让每一步都是整数的.如本题..由于最后的答案要mod一个数...当要做除法时..直接用逆元就好..若没有取模运算..那么就先对所有行做lcm...然后再消...


Program:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<time.h>
#include<map>
#include<algorithm>
#define ll long long
#define eps 1e-5
#define oo 10007
#define pi acos(-1.0)
#define MAXN 305
using namespace std;   
struct node
{
      int x,y;
      node () {}
      node ( int x , int y ) : x(x) , y(y) {}
}p[MAXN];
typedef node Vector;
Vector operator - ( node a , node b ) { return Vector ( a.x - b.x , a.y - b.y ); }

int Dot ( Vector a , Vector b )
{
    return a.x*b.x + a.y * b.y;
}

int dis(node p1,node p2)
{
      return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
}

int Cross (Vector a , Vector b )
{
    return a.x*b.y - a.y*b.x;
}

bool InLine(node p1,node p2,node p3)
{
      return Cross ( p1 - p2 , p3 - p2 ) == 0 && Dot ( p1 - p2 , p3 - p2 ) < 0;
      
}
int C[MAXN][MAXN]; 
void exgcd(int a,int b,int &x,int &y)
{  
      if(b==0) { x=1,y=0; return; }
      exgcd(b,a%b,x,y);  
      int t;  
      t=x,x=y,y=t-a/b*y;  
      return;  
} 
int det(int n)//计算n阶行列式的绝对值%mod  
{  
      int ans=1,flag=1;//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]%oo;//对角线相乘  
           int x,y;  
           exgcd(C[i][i],oo,x,y);//x为逆元.若没有取模运算那就需要LCM再来化 
           for(k=i+1;k<n;k++) C[i][k]=C[i][k]*x%oo;  
           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])%oo;  
                    C[j][k]=(C[j][k]+oo)%oo;  
              }  
      }  
      ans=(ans%oo+oo)%oo;  
      if(flag) return ans;  
         else  return oo-ans;  
}    
int main()
{     
      int T,N,R,i,j,x; 
      scanf("%d",&T);
      while (T--)
      {
             scanf("%d%d",&N,&R);
             R*=R;
             for (i=0;i<N;i++) scanf("%d%d",&p[i].x,&p[i].y);
             memset(C,0,sizeof(C));
             for (i=0;i<N;i++)
                for (j=i+1;j<N;j++)
                   if (dis(p[i],p[j])<=R)
                   {
                         for (x=0;x<N;x++)
                            if (InLine(p[i],p[x],p[j])) goto A;
                         C[i][i]++,C[j][j]++;
                         C[i][j]=C[j][i]=-1;
                         A: ;
                   } 
             x=det(N-1);
             if (!x) x=-1;
             printf("%d\n",x);
      }
      return 0;
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值