现在的位置: 首页 > 综合 > 正文

多小第一场:hdu:4305(矩阵求生成树的个数+乘法逆元)

2018年05月01日 ⁄ 综合 ⁄ 共 2748字 ⁄ 字号 评论关闭

      题目模型:给定平面上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表示。


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

抱歉!评论已关闭.