高消和逆元的理解,hdu4305【From:2012 MultiUniversity 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。

 

 

原文地址:https://www.cnblogs.com/celia01/p/2622469.html