标签:
题目: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