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

     

     

  • 相关阅读:
    单片机编程时易错总结 20181015 项目:3060-A
    UCOS 多任务系统中需要注意的地方 一个任务至少要有一些执行内容
    LDO-AMS1117
    DCDC与LDO
    电容器的ESR
    Class AB与Class D功放
    驻极体麦克风
    音频处理的技术指标
    I2S接口工作原理
    有关功放输入端的探究
  • 原文地址:https://www.cnblogs.com/celia01/p/2622469.html
Copyright © 2020-2023  润新知