1. 程式人生 > 實用技巧 >題解 P4208 【[JSOI2008]最小生成樹計數】

題解 P4208 【[JSOI2008]最小生成樹計數】

題目連結

Solution [JSOI2008]最小生成樹計數

題目大意:給定一張\(n\)個點\(m\)條邊無向圖,保證具有相同邊權的邊不超過\(10\)條,\(n \leq100,m\leq1000\),求不同最小生成樹個數

最小生成樹、計數


分析:

做這題需要用到一個結論:

所有最小生成樹,把邊權按照升序排列之後,得到的序列是一樣的。也就是說對於最小生成樹而言,每種邊權的邊選了多少條是確定的

此外,按照邊權升序的順序加邊,把一種邊權的邊全部加完之後,所有生成樹的連通性是相同的。

由於具有相同邊權的邊的數量較少我們可以暴力

先跑一遍最小生成樹求出每種邊權選擇的數量,然後對每種邊權暴力統計合法方案數,最後用乘法原理計算答案

判斷兩個圖連通性是否相同可以使用並查集,如果\(A\)圖每個點所在的聯通塊編號序列可以通過某種置換得到\(B\)圖對應序列,那麼\(A,B\)兩個圖的連通性就是相同的

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 128,maxm = 1024,mod = 31011;
inline int read(){
    int x = 0;char c = getchar();
    while(!isdigit(c))c = getchar();
    while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    return x;
}
struct edge{
    int u,v,d;
    bool operator < (const edge &rhs)const{
        return d < rhs.d;
    }
}edges[maxm];
int n,m,tot,last,num,mem[16],trans[maxn],ans = 1,cnt;
struct mset{
    int f[maxn],sz;
    inline void init(){
        for(int i = 1;i <= n;i++)f[i] = i;
        sz = n;
    }
    inline int find(int x){return x == f[x] ? x : f[x] = find(f[x]);}
    inline void merge(int a,int b){
        int x = find(a),y = find(b);
        f[x] = y;sz--;
    }
}s,tmp,lass;
inline void out(){
    tmp = lass;
    for(int i = 1;i <= num;i++){
        int x = tmp.find(edges[mem[i]].u),y = tmp.find(edges[mem[i]].v);
        if(x == y)return;
        tmp.merge(x,y);
    }
    for(int i = 1;i <= n;i++)tmp.f[i] = tmp.find(tmp.f[i]);
    memset(trans,0,sizeof(trans));
    for(int i = 1;i <= n;i++){
        if(!trans[lass.f[i]])trans[lass.f[i]] = tmp.f[i];
        if(trans[lass.f[i]] != tmp.f[i])return;
    }
    cnt++;
}
inline void dfs(int now,int pos,int mx){
    if(pos == num + 1){
        out();
        return;   
    }
    if((mx - now + 1) < (num - pos + 1))return;
    mem[pos] = now;
    dfs(now + 1,pos + 1,mx);
    dfs(now + 1,pos,mx);
}
int main(){
    n = read(),m = read();
    for(int i = 1;i <= m;i++)edges[i].u = read(),edges[i].v = read(),edges[i].d = read();
    sort(edges + 1,edges + 1 + m);
    s.init();
    lass = s;
    for(int i = 1;i <= m + 1;i++){
        if(edges[i].d != edges[i - 1].d){
            for(int i = 1;i <= n;i++)s.f[i] = s.find(s.f[i]);
            cnt = 0;
            dfs(last,1,i - 1);
            ans = (ans * cnt) % mod;
            last = i;
            lass = s;
            num = 0;
        }
        int x = s.find(edges[i].u),y = s.find(edges[i].v);
        if(x == y)continue;
        num++;
        s.merge(x,y);
    }
    printf("%d\n",s.sz == 1 ? ans : 0);
    return 0;
}