• 【生成树计数】专题总结


    在CTSC和APIO上好像经常听到生成树计数这东西

    于是就去看了下论文

    蒟蒻表示看不懂证n明orz 反正懂用就行了。。

    生成树计数

    生成树计数就是给出一种n个点的无向图G 求这n个点的生成树个数

    G的度数矩阵d[i][j] 当i≠j时d[i][j]=0 否则等于i点的度数

    G的邻接矩阵a[i][j] a[i][j]=i、j间的边的个数(能有重边)

    Kirchhoff矩阵 c[i][j]=d[i][j]-a[i][j]

    Kirchhoff矩阵的n-1阶主子式的行列式的值的绝对值即为生成树的个数

    n-1阶主子式即为把原矩阵去掉任意第i行和第i列得到的矩阵

    行列式的值的计算

    行列式的算法一般是用高斯消元计算出上三角矩阵 再把主对角线的值累成起来

    但是高斯消元可能会导致出现小数

    于是我们用一种类似求gcd的方法 辗转消除即可

    这里要注意 如果交换矩阵的某两行 行列式的值要*-1 (虽然对生成树计数的计算没影响- -)

    最小生成树计数代码

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 using namespace std;
      5 const int N=101,M=1001,mo=31011;
      6 struct info{
      7     int x,y,lon;
      8     info(const int a=0,const int b=0,const int c=0):
      9         x(a),y(b),lon(c){}
     10 }sl[M];
     11 struct inli{
     12     int next,data;
     13     inli(const int a=0,const int b=0):
     14         next(a),data(b){}
     15 }line[M*2];
     16 int n,m,nl,num,change[N],save[N],bo[N],link[N][N],son[N],jz[N][N],fat[N],d[N],ans=1;
     17 inline bool cmp(info x,info y){ return x.lon<y.lon; }
     18 void clean(){
     19     memset(jz,0,sizeof(jz));
     20     memset(bo,0,sizeof(bo));
     21     memset(son,0,sizeof(son));
     22     nl=0;
     23 }
     24 int getfat(int t){ return fat[t] ? fat[t]=getfat(fat[t]) : t; }
     25 void search(int t){
     26     bo[t]=2,save[++save[0]]=t;
     27     for (int i=son[t];i;i=line[i].next)
     28     if (bo[line[i].data]==1) search(line[i].data);
     29 }
     30 void swap(int x,int y){
     31     for (int i=1;i<=num;i++){ int t=jz[x][i]; jz[x][i]=jz[y][i],jz[y][i]=t; }
     32 }
     33 void gcd(int x,int y,int t){
     34     while (jz[y][t]){
     35         if (abs(jz[x][t])>abs(jz[y][t])) swap(x,y);
     36         //abs
     37         int xx=jz[y][t]/jz[x][t];
     38         for (int j=t;j<=num;j++) jz[y][j]=(jz[y][j]-jz[x][j]*xx%mo)%mo;
     39         //need %mo
     40     }
     41 }
     42 int getans(){
     43     int res=1;
     44     num=save[0]-1;
     45     for (int i=1;i<=num;i++){
     46         if (!jz[i][i])
     47         for (int j=i+1;j<=num;j++)
     48         if (jz[j][i]){
     49             swap(i,j);
     50             break;
     51         }
     52         for (int j=i+1;j<=num;j++)
     53         if (jz[j][i]) gcd(i,j,i);
     54     }
     55     for (int i=1;i<=num;i++) res=res*jz[i][i]%mo;
     56     return res<0 ? -res : res;
     57 }
     58 void makeans(){
     59     for (int i=1;i<=n;i++)
     60     if (bo[i]==1){
     61         save[0]=0;
     62         search(i);
     63         for (int i=1;i<=save[0];i++)
     64         for (int j=1;j<=save[0];j++) jz[i][j]=0;
     65         change[0]=0;
     66         for (int i=1;i<=save[0];i++) change[save[i]]=++change[0];
     67         for (int j=1;j<=save[0];j++){
     68             int now=save[j];
     69             for (int k=son[now];k;k=line[k].next){
     70                 ++jz[change[now]][change[now]];
     71                 --jz[change[now]][change[line[k].data]];
     72             }
     73         }
     74         ans=ans*getans()%mo;
     75         for (int j=2;j<=save[0];j++) fat[save[j]]=save[1];
     76     }
     77 }
     78 void work(){
     79     sort(sl+1,sl+m+1,cmp);
     80     for (int i=1;i<=m;){
     81         int save=sl[i].lon;
     82         clean();
     83         for (;i<=m && sl[i].lon==save;++i){
     84             int x=getfat(sl[i].x),y=getfat(sl[i].y);
     85             if (x==y) continue;
     86             bo[x]=bo[y]=1;
     87             line[++nl]=inli(son[x],y),son[x]=nl;
     88             line[++nl]=inli(son[y],x),son[y]=nl;
     89         }
     90         makeans();
     91     }
     92 }
     93 int main(){
     94     freopen("bz1016.in","r",stdin);
     95     freopen("bz1016.out","w",stdout);
     96     scanf("%d%d",&n,&m);
     97     for (int x,y,z,i=1;i<=m;i++){
     98         scanf("%d%d%d",&x,&y,&z);
     99         sl[i]=info(x,y,z);
    100     }
    101     work();
    102     memset(bo,0,sizeof(bo));
    103     int add=0;
    104     for (int i=1;i<=n;i++)
    105     if (!bo[getfat(i)]) add+=bo[getfat(i)]=1;
    106     if (add>1) puts("0");
    107     else printf("%d",ans);
    108     fclose(stdin);
    109     fclose(stdout);
    110 }
    View Code
  • 相关阅读:
    10.01 简单的51代码
    1010Linux的文件操作函数以及所需头文件
    10.05 最初对Linux的了解,对Shell的认识
    1006 Linux的基本命令以及一些简单的通配符说明
    10.03 简单的51单片机程序
    1011Linux用户管理规则及用户管理函数
    vim命令以及gcc编译器的常用cmd
    10.02 一个简单的串口的初始化程序
    做销售的100个绝招
    一个女程序员的创业人生:胆识也是一种能力
  • 原文地址:https://www.cnblogs.com/g-word/p/3729361.html
Copyright © 2020-2023  润新知