1. 程式人生 > >[bzoj1016] [JSOI2008]最小生成樹計數

[bzoj1016] [JSOI2008]最小生成樹計數

() -i %d amp i++ pan bzoj 算法 turn

Description

  現在給出了一個簡單無向加權圖。你不滿足於求出這個圖的最小生成樹,而希望知道這個圖中有多少個不同的最小生成樹。(如果兩顆最小生成樹中至少有一條邊不同,則這兩個最小生成樹就是不同的)。由於不同的最小生成樹可能很多,所以你只需要輸出方案數對31011的模就可以了。

Input

  第一行包含兩個數,n和m,其中1<=n<=100; 1<=m<=1000; 表示該無向圖的節點數和邊數。每個節點用1~n的整數編號。接下來的m行,每行包含兩個整數:a, b, c,表示節點a, b之間的邊的權值為c,其中1<=c<=1,000,000,000。數據保證不會出現自回邊和重邊。註意:具有相同權值的邊不會超過10條。

Output

  輸出不同的最小生成樹有多少個。你只需要輸出數量對31011的模就可以了。

Sample Input

4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1

Sample Output

8

Solution

考慮到最小生成樹的算法\(Kruskal\)的過程,顯然可以發現,對於一個圖的不同的最小生成樹,邊權一樣的邊使用的次數也是一樣的。

註意到邊權一樣的邊最多只有\(10\)條,那麽可以對於每種邊權爆搜出一共有多少種情況,然後每種邊權是獨立的,乘起來就好了。

註意特判沒有最小生成樹的情況。

#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

#define write(x) printf("%d\n",x)

const int maxn = 2e5+10;
const int mod = 31011;

int n,m,fa[maxn],cnt,tot,ans=1,sum;
struct edge{
    int u,v,w;
    bool operator < (const edge &rhs) const {return w<rhs.w;}
}e[maxn];
struct data{int l,r,num;}s[maxn];

int find(int x) {return fa[x]==x?x:find(fa[x]);}

void dfs(int x,int now,int w) {
    if(now==s[x].r+1) return sum+=w==s[x].num,void();
    dfs(x,now+1,w);
    int u=find(e[now].u),v=find(e[now].v);
    if(u!=v) fa[u]=v,dfs(x,now+1,w+1),fa[u]=u,fa[v]=v;
}

int main() {
    read(n),read(m);
    for(int i=1;i<=m;i++) read(e[i].u),read(e[i].v),read(e[i].w);
    for(int i=1;i<=n;i++) fa[i]=i;
    sort(e+1,e+m+1);
    for(int i=1;i<=m;i++) {
        if(e[i].w!=e[i-1].w) s[++cnt].l=i;
        if(e[i].w!=e[i+1].w) s[cnt].r=i;
        int u=find(e[i].u),v=find(e[i].v);
        if(u!=v) fa[u]=v,s[cnt].num++,tot++;
    }
    if(tot!=n-1) return puts("0"),0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=cnt;i++) {
        sum=0;dfs(i,s[i].l,0);ans=1ll*ans*sum%mod;
        for(int j=s[i].l;j<=s[i].r;j++) {
            int u=find(e[j].u),v=find(e[j].v);
            if(u!=v) fa[u]=v;
        }
    }
    write(ans);
    return 0;
}

[bzoj1016] [JSOI2008]最小生成樹計數