高消和逆元的理解,hdu4305【From:2012 Multi-University Training Contest 1】

题意很简单,给出一个无向图,求其中生成树的个数。这里用到了一个叫(Kirchhoff's matrix tree theorem)的生成树计数原理,具体不知道是什么,柯队介绍上说的。具体可以参见http://wtommy.ycool.com/post.2218850.html

按照定理,可以得到一个行列式,求值即可。知道以下几个知识就可以了:

1、行列式的值与矩阵的变换关系有密切联系,即可以通过高斯消元的方法求出;

2、求值过程中用到了行列式的三个性质:

  对换行列式的任意两行(列),行列式的值变号;

  行列式的某一行(列)中所有元素都乘以同一个数k,等于用数k乘以此行列式的值;

  把行列式的某一行(列)的各元素都乘以同一数后加到另一行(列)对应元素上,行列式的值不变。!!!(记住,这样操作最后的值是不变的)

最后把高消过程中乘上的数,都放到分母上,求逆元既可以得到结果。

注意:整个过程中所有操作都要不断的模数,这样才不会溢出。

高斯消元的过程消去的过程可以模数,这个原理无法说得太清,就是感觉只有加、减、乘,所以模了模也还是同余的。对于所有的除数,也需要不断的缩小,不然如果最后乘到一起再求逆元,肯定早已溢出了,原则上 a/(b*c*d)Ξa*b-1*c-1*d-1 (mod p),但是如果p是素数,也可以先得到eΞb*c*d (mod p),再求 a/(b*c*d)Ξa/eΞa*e-1 (mod p),这道题的p为10007,是一个素数,所以两种方法都可以求解。

  1 #include<iostream>

  2 #include<cstdio>

  3 #include<cmath>

  4 #include<algorithm>

  5 #include<cstring>

  6 #include<cmath>

  7 #define see(x) cout<<#x<<":"<<x<<endl;

  8 using namespace std;

  9 const int maxn = 305;

 10 const int mod = 10007;

 11 const double eps = 1e-6;

 12 struct Point{

 13     double x, y;

 14 }p[maxn];

 15 inline double dis(Point a, Point b){

 16     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));

 17 }

 18 inline int gcd(int a, int b){

 19     return b==0 ? a:gcd(b,a%b);

 20 }

 21 inline int lcm(int a, int b){

 22     return a/gcd(a,b)*b;

 23 }

 24 inline int extgcd(int a, int b, int &x, int &y){

 25     if(!b) {x=1;y=0;return a;}

 26     int res = extgcd(b,a%b,x,y);

 27     int t = x; x=y; y = t-a/b*y;

 28     return res;

 29 }

 30 int solve(int a, int b){

 31     int x, y;

 32     extgcd(a,b,x,y);

 33     return (x%b+b)%b;

 34 }

 35 int aa[maxn][maxn], bb[maxn][maxn];

 36 int equ, var;

 37 int Gauss(){

 38     int i, j, k, max_r, col;

 39     int ta, tb, LCM;

 40     int temp, md = 1;

 41     col = 0;

 42     for(k=0;k<equ&&col<var;k++,col++){

 43         max_r = k;

 44         for(i=k+1;i<equ;i++){

 45             if(abs(aa[i][col])>abs(aa[max_r][col]))

 46                 max_r = i;

 47         }

 48         if(max_r!=k){

 49             for(j=k;j<var;j++) swap(aa[k][j],aa[max_r][j]);

 50             md *= -1;

 51         }

 52         if(aa[k][col]==0){

 53             k--;

 54             continue;

 55         }

 56         for(i=k+1;i<equ;i++){

 57             if(aa[i][col]!=0){

 58                 LCM = lcm(abs(aa[i][col]),abs(aa[k][col]));

 59                 ta = LCM/abs(aa[i][col]), tb = LCM/abs(aa[k][col]);

 60                 if(aa[i][col]*aa[k][col]<0) tb = -tb;

 61                 md = ((md*solve(ta,mod))%mod+mod)%mod;

 62                 for(j=col;j<var;j++){

 63                     aa[i][j] = ((aa[i][j]*ta-aa[k][j]*tb)%mod+mod)%mod;

 64                 }

 65             }

 66         }

 67     }

 68     return md;

 69 }

 70 void Init(int n){

 71     int i, j, k;

 72     memset(aa,0,sizeof(aa));

 73     memset(bb,0,sizeof(bb));

 74 }

 75 int main(){

 76     int t, i, j, k, l, n, r, ans1, ans2, x, y, flag, res;

 77     scanf("%d",&t);

 78     while(t--){

 79         scanf("%d%d",&n,&r);

 80         for(i=0;i<n;i++){

 81             scanf("%lf%lf",&p[i].x,&p[i].y);

 82         }

 83         Init(n);

 84         for(i=0;i<n;i++){

 85             for(j=i+1;j<n;j++){

 86                 if(dis(p[i],p[j])<=r){

 87                     flag = 1;

 88                     for(k=0;k<n;k++){

 89                         if(k!=i&&k!=j){

 90                             if((p[k].x<p[j].x&&p[k].x>p[i].x)||(p[k].x<p[i].x&&p[k].x>p[j].x)){

 91                                 if(fabs((p[i].y-p[k].y)*(p[j].x-p[k].x)-(p[j].y-p[k].y)*(p[i].x-p[k].x))<eps){

 92                                     flag = 0;

 93                                     break;

 94                                 }

 95                             }

 96                         }

 97                     }

 98                     if(flag){

 99                         bb[i][j] = 1;

100                         aa[i][i]++; aa[j][j]++;

101                     }

102                 }

103             }

104         }

105         for(i=0;i<n;i++){

106             for(j=i+1;j<n;j++){

107                 if(bb[i][j]){

108                     aa[i][j] = -1;

109                     aa[j][i] = -1;

110                 }

111             }

112         }

113         equ = var = n-1;

114         ans2 = Gauss();

115         ans1 = 1;

116         for(i=0;i<n-1;i++){

117             ans1 = (ans1*aa[i][i])%mod;

118         }

119         res = ((ans1*ans2)%mod+mod)%mod;

120         if(res){

121             printf("%d\n",res);

122         }

123         else{

124             printf("-1\n");

125         }

126     }

127     return 0;

128 }

这道题,还卡了很久,一个是对行列式的性质不熟悉,另外对于逆元的真正意义也不熟悉,不肯定自己的做法。

现在更正确认一下,若一个数对模p求逆元,一般只能是正数,一是负数无意义,而是如果用负数可能得到错解(一般extgcd模板也不适用)

另外(b%p+p)%p,这种消除负数的方法一般还是能用的(至少目前没发现有编译器和oj不通过),最后附上几个常见的逆元规律:

1、一个数n对模p求逆元,如果n%p==0,则求的的逆元为0,其他的情况,最终的值会在1~p-1之间;

2、1对任何p求逆元,求的的值都为1。

 

 

你可能感兴趣的:(test)