题解 [JSOI2008]最小生成树计数

某个ZZ查资料得到的经验教训——

  1. 某个算法有中文名字就不要去查原名(╯‵□′)╯︵┻━┻
  2. 除非你要看证明过程或者是写博客,否则有些东西,不一定要去看维基百科OR论文。毕竟看到一些数学类的知识,头疼<(_ _)>
  3. 要看清题号再做题◑﹏◐,否则就会找到一道(自认为)很难的题,然后自闭挡四防御力up
  4. 不要熬夜U•ェ•*U

转送门----->洛谷/bzoj

具体内容代码见

  1 #include<cstring>
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<algorithm>
  5 using namespace std;
  6 /*
  7     大致思路————首先看能否生成最小生成树
  8                其次找最小生成树中可以被替换的权值,每次枚举一种,
  9                把其他权值进行缩点。由于我们不在意其他边究竟是什么,所以只要知道是否联通即可
 10                后再辅以矩阵树定理和乘法原理求值
 11     前置知识————1.最小生成树
 12                2.矩阵树定理
 13                3.分块的基本思想(其实不是必需的QAQ,现理解也可以)
 14                4.乘法原理
 15                (划掉)5.数学计算并且不会算错数(划掉)
 16                9.磅15便士
 17  */
 18 namespace the_Death{
 19     const int maxn=1e3+5,MAXN=1e3+5,mod=31011;
 20     int n,m,tot,fa[maxn],a[maxn][maxn],del[maxn],val[maxn];
 21     //val[maxn]->指的是有多少不重复的值
 22     //del[maxn]->是指缩点重构后的图
 23     //a[maxn][maxn]->基尔霍夫矩阵
 24     struct ziji{
 25         int x,y,z;
 26         bool operator <(const ziji&b)const{return z<b.z;}
 27         #define x(i) mn[i].x
 28         #define y(i) mn[i].y
 29         #define z(i) mn[i].z
 30         #define u(i) tree[i].x
 31         #define v(i) tree[i].y
 32         #define w(i) tree[i].z
 33     }mn[maxn],tree[MAXN];
 34     //mn[maxn]->是原先保存图的数组
 35     //tree[maxn]->是最小生成树中树的构成点的图
 36     inline int find(int x){return fa[x]==x?x:find(fa[x]);}
 37     inline void it(){for(register int i=1;i<=n;i++)fa[i]=i;}
 38     inline void add(int x,int y){
 39         --a[x][y],--a[y][x],++a[x][x],++a[y][y];
 40         //构造基尔霍夫矩阵
 41     }
 42     inline void merge(int x,int y){fa[find(x)]=find(y);}
 43     inline int gauss(int n){
 44         //利用辗转相除术进行高斯消元
 45         //之所以用辗转相除,只为了保持精度
 46         int ans=1;
 47         for(register int i=1;i<=n;i++){
 48             for(register int k=i+1;k<=n;k++){
 49                 while(a[k][i]){
 50                     int d=a[i][i]/a[k][i];
 51                     for(register int j=i;j<=n;j++) 
 52                         a[i][j]=(a[i][j]-1LL*d*a[k][j]%mod+mod)%mod;
 53                     swap(a[i],a[k]),ans=-ans;
 54                     //交换行,行列式改变
 55                 }
 56             }
 57             ans=1ll*ans*a[i][i]%mod,ans=(ans+mod)%mod;
 58             //乘法原理累计
 59         }
 60         return ans;
 61     }
 62     inline bool kruskal(){
 63         sort(tree+1,tree+1+m);it();
 64         int cnt=0;
 65         for(register int i=1;i<=m;i++){
 66             int root=find(u(i)),root1=find(v(i));
 67             if(root==root1) continue;
 68             fa[root]=root1,mn[++cnt]=tree[i];
 69             //把用到的点放到mn[maxn]中去
 70             if(w(i)!=val[tot]) val[++tot]=w(i);
 71             //记录不重复的点权
 72         }
 73         return cnt==n-1;
 74     }
 75     inline void addedge(int v){
 76         for(register int i=1;i<n&&z(i)!=v;i++) merge(x(i),y(i));
 77         for(register int i=n-1;i&&z(i)!=v;i--) merge(x(i),y(i));
 78         //双向更新新树的联通  NOTICE:YOU MUST DO IT LIKE THIS
 79 
 80     }
 81     inline int getblock(){
 82         //得到它缩点后有多少个点
 83         int blo=0;
 84         for(register int i=1;i<=n;i++) 
 85             if(find(i)==i) del[i]=++blo;
 86         for(register int i=1;i<=n;i++) del[i]=del[find(i)];
 87         //类似于分块的操作
 88         return blo;
 89     }
 90     inline void rebuild(int v){
 91         //每次都要去找一个边权,看他有几种转化的边,所以每一次都要rebuild
 92         memset(a,0,sizeof(a));
 93         for(register int i=1;i<=m;i++)
 94             if(w(i)==v) add(del[u(i)],del[v(i)]);
 95             //如果找到了,那你就记录构造基尔霍夫矩阵
 96     }
 97     int main(){
 98         scanf("%d%d",&n,&m);int ans=1;
 99         for(register int i=1;i<=m;i++)
100             scanf("%d%d%d",&u(i),&v(i),&w(i));
101         if(!kruskal()){puts("0");system("pause");return 0;}
102         for(register int i=1;i<=tot;i++){
103             it();addedge(val[i]);int blo=getblock();
104             rebuild(val[i]);ans=1ll*ans*gauss(blo-1)%mod;
105         }
106         printf("%d\n",ans);system("pause");return 0;
107     }
108 }
109 int main(){
110     the_Death::main();return 0;
111 }
View Code

猜你喜欢

转载自www.cnblogs.com/fallen-down/p/11205009.html