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

hdu4305Lightning 生成树计数(基尔霍夫矩阵)+高斯消元+逆元

时间:2015-07-25 18:35:41      阅读:120      评论:0      收藏:0      [点我收藏+]

标签:

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4305

题意:比较裸的生成树计数问题。

  如何处理生成树计数问题?

基尔霍夫矩阵:

if i==j  Kir[i][j] = i的度数

if i!=j   Kir[i][j] = i到j的平行边的个数的负数

即,基尔霍夫矩阵 = 度数矩阵 - 邻接矩阵


基尔霍夫矩阵删去第i行和第i列,余下i-1阶的行列式的值即为生成树个数。(证明略)


求行列式的值可以将行列式转为上三角阵,求对角线上的积即为行列式的值。

可以用高斯消元的方法转换上三角阵,过程中要mod p,除法改为逆元。


代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 400+10;
typedef long long ll;
const ll mod = 10007;

struct point{
    int x,y;
    int id;
}p[N];
bool d[N][N],f[N][N];
int T,n,r;
int flag = 0;
ll dis(point a,point b){
    return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
int online(point a,point b,point c){
    return (ll)(a.x-c.x)*(b.y-c.y) == (ll)(b.x-c.x)*(a.y-c.y);
}
void build(){
    memset(d,0,sizeof(d));

    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++) if(i!=j){
        if(dis(p[i],p[j]) <= (ll)r*r){
            int ok = 1;
            for(int k=0;k<n;k++)if(k!=i&&k!=j){
                if( (p[i].x<=p[k].x && p[k].x<=p[j].x)||(p[j].x<=p[k].x && p[k].x<=p[i].x) ){
                    if(online(p[i],p[j],p[k])){
                        ok = 0;
                        break;
                    }
                }
            }
            if(ok) d[i][j]=1;
        }
    }

    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++) f[i][j]=d[i][j];

    for(int k=0;k<n;k++)
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
        f[i][j] = f[i][j] | (f[i][k]&&f[k][j]);

    flag = 1;
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    if(!f[i][j]) flag = 0;
}

ll exgcd(ll a,ll b,ll &x,ll &y)//乘法逆元返回的d是a,b的公约数,x是a mod b的逆元
{
    if(b==0)
    {
        x=1ll;y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*y;
    return d;
}
ll Gauss(int C[][N],int n)//计算n阶行列式的绝对值 % mod
{
    ll ans=1ll;
    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;//对角线相乘
      ll 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]-(ll)C[j][i]*C[i][k])%mod;
          if(j==k)
          C[j][k]=(C[j][k]+mod)%mod;
      }
      for(k=i+1;k<n;k++)
        C[i][k]=(ll)C[i][k]*C[i][i]%mod;

    }


    ans=(ans%mod+mod)%mod;
    if(flag) return ans;
    else  return mod-ans;
}

int Kir[N][N];
int solve(){
    memset(Kir,0,sizeof(Kir));
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
        Kir[i][i] += d[i][j];
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
        Kir[i][j] -= d[i][j];

    return Gauss(Kir,n-1);
}
int main(){
    cin >> T;
    while(T--){
//        cin >> n >> r;
        scanf("%d%d",&n,&r);
        for(int i=0;i<n;i++){
            scanf("%d%d",&p[i].x,&p[i].y);
            p[i].id = i;
        }
        build();
        if(flag==0){
            puts("-1");
            continue;
        }
        ll ans = solve();
//        cout<<ans<<endl;
        printf("%d\n",(int)ans);
    }
    return 0;
}



版权声明:本文为博主原创文章,未经博主允许不得转载。

hdu4305Lightning 生成树计数(基尔霍夫矩阵)+高斯消元+逆元

标签:

原文地址:http://blog.csdn.net/alpc_wt/article/details/47057517

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