• 高消和逆元的理解,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。

     

     

  • 相关阅读:
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    asp.net session对象的持久化
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    小白也能看懂的约瑟夫环问题
  • 原文地址:https://www.cnblogs.com/celia01/p/2622469.html
Copyright © 2020-2023  润新知