1. 程式人生 > >【BZOJ】1016: [JSOI2008]最小生成樹計數

【BZOJ】1016: [JSOI2008]最小生成樹計數

ont node bits back 然而 rst cst truct oid

題解

考慮kruskal
我們都是從邊權最小的邊開始取,然後連在一起

那我們選出邊權最小的一堆邊,然後這個圖就分成了很多聯通塊,把每個聯通塊內部用矩陣樹定理算一下生成樹個數,再把聯通塊縮成一個大點,重復取下一個邊權的邊進行操作

好想然而不是很好寫= =寫起來感覺有點麻煩

模數非質數,用long double水一下過掉了

代碼

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <cmath> #include <bitset> #include <queue> #define enter putchar(‘\n‘) #define space putchar(‘ ‘) //#define ivorysi #define pb push_back #define mo 974711 #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define MAXN 200005 #define eps 1e-12
using namespace std; typedef long long int64; typedef long double db; template<class T> void read(T &res) { res = 0;char c = getchar();T f = 1; while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) f = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) { res = res * 10
- ‘0‘ + c; c = getchar(); } res = res * f; } template<class T> void out(T x) { if(x < 0) {x = -x;putchar(‘-‘);} if(x >= 10) out(x / 10); putchar(‘0‘ + x % 10); } int N,M; struct node { int u,v,c; friend bool operator < (const node &a,const node &b) { return a.c < b.c; } }E[1005]; int fa[105],id[105],f[105][105],L[105],tot,D[105]; bool vis[105]; db g[105][105]; int64 ans = 1; int getfa(int x) { return x == fa[x] ? x : fa[x] = getfa(fa[x]); } db Guass(int n) { db res = 1; for(int i = 2 ; i <= n ; ++i) { int l = i; for(int j = i + 1 ; j <= n ; ++j) { if(fabs(g[j][i]) > fabs(g[l][i])) l = j; } if(i != l) { res = -res; for(int j = i ; j <= n ; ++j) { swap(g[i][j],g[l][j]); } } if(fabs(g[i][i]) == 0) return 0; for(int j = i + 1 ; j <= n ; ++j) { db t = g[j][i] / g[i][i]; for(int k = i ; k <= n ; ++k) { g[j][k] -= g[i][k] * t; } } } for(int k = 2 ; k <= n ; ++k) { res = res * g[k][k]; } return res; } void dfs(int u,int n) { vis[u] = 1; L[++tot] = u; D[u] = tot; for(int i = 1 ; i <= n ; ++i) { if(f[u][i]) { if(!vis[i]) dfs(i,n); } } } void Process(int l,int r) { memset(id,0,sizeof(id)); memset(f,0,sizeof(f)); memset(vis,0,sizeof(vis)); int cnt = 0; for(int i = 1 ; i <= N ; ++i) { if(!id[getfa(i)]) { id[getfa(i)] = ++cnt; } } for(int i = l ; i <= r ; ++i) { int s = getfa(E[i].u),t = getfa(E[i].v); if(s == t) continue; f[id[s]][id[t]]++; f[id[t]][id[s]]++; } for(int i = 1 ; i <= cnt ; ++i) { if(!vis[i]) { tot = 0; dfs(i,cnt); if(tot == 1) continue; memset(g,0,sizeof(g)); for(int j = 1 ; j <= tot ; ++j) { int u = L[j]; for(int k = 1 ; k <= cnt ; ++k) { if(f[u][k]) { g[j][j] += f[u][k]; g[j][D[k]] -= f[u][k]; } } } ans = ans * ((int64)(Guass(tot) + 0.5) % 31011) % 31011; } } for(int i = l ; i <= r ; ++i) { fa[getfa(E[i].v)] = getfa(E[i].u); } } void Solve() { read(N);read(M); for(int i = 1 ; i <= N ; ++i) fa[i] = i; int u,v,c; for(int i = 1 ; i <= M ; ++i) { read(E[i].u);read(E[i].v);read(E[i].c); fa[getfa(E[i].u)] = getfa(E[i].v); } for(int i = 2 ; i <= N ; ++i) { if(getfa(i) != getfa(i - 1)) { puts("0"); return; } } for(int i = 1 ; i <= N ; ++i) fa[i] = i; sort(E + 1,E + M + 1); v = 0;int st = 0; bool flag = 0; for(int i = 1 ; i <= M ; ++i) { if(E[i].c != v) { if(st != 0) Process(st,i - 1); st = i; v = E[i].c; flag = 1; for(int j = 2 ; j <= N ; ++j) { if(getfa(j) != getfa(j - 1)) {flag = 0;break;} } if(flag) break; } } if(!flag) Process(st,M); out(ans);enter; } int main() { #ifdef ivorysi freopen("f1.in","r",stdin); #endif Solve(); }

【BZOJ】1016: [JSOI2008]最小生成樹計數