题意很简单,给出一个无向图,求其中生成树的个数。这里用到了一个叫(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。