bzoj 1016 最小生成樹計數

2022-06-02 12:39:12 字數 1974 閱讀 1651

給定乙個簡單無向有權圖,求其最小生成樹的個數。

在我們用kruskal計算最小生成樹時,由於相同權值的邊選擇的順序是隨機的,所以我們最小生成樹就也許有很多。

對於同一權值的邊,我們不論用什麼順序「掃過」,最終的得到的無向森林的連通性一定是一樣的,即對後面的邊是否加入的影響也是一樣的,所以可以根據這一點將最小生成樹分階段統計,所有權值相同的邊為一階段,每個階段都有乙個方案數,最終的答案便是方案數的乘積。

對於某一階段的乙個選邊的合法方案是什麼呢?就是這些邊加入到圖中,使圖的連通性和「按任意順序掃一遍,能加就加」後的連通性一樣。

至於怎麼計算,先掃一遍,將減少的連通塊的數量記下來,然後撤銷操作,列舉邊集(2^10),判斷該邊集加入後減少的聯通快是否一樣,一樣就合法。

也可以將到達這一階段時,圖中的聯通快縮成乙個點,然後計算當前階段的邊和縮了點後的圖的聯通快,每個連通塊計算生成樹個數,它們的乘積就是本階段的方案數

我用的是第二種方法,有點難寫

1 #include 2 #include 3 #include 4 #include 5 #include 6

#define abs(a) ((a)<0?-(a):(a))

7#define m 31011

8#define maxn 110

9using

namespace

std;

1011

struct

edge

14bool

operator

17};

1819 typedef int

matrix[maxn][maxn];

2021

intn, m;

22 vectoredge;

2324

intfa[maxn];

2526

void

init()

29int find( int

a )

32void unon( int a, int

b )

3738

int det( matrix a, int

n ) 54}

55}56}

57int rt = 1;58

for( int i=0; i)

59 rt = (rt*a[i][i])%m;

60return

abs(rt);61}

6263

int subfa[30

], cnt;

64matrix a;

65int sfind( int

a )

68void sunon( int a, int

b )

74int pm( map &mp, int i )

75int calc( const vector&e )

84for( int i=0; ii;

85for( int i=0; i)

89int rt = 1;90

for( int i=0; i)

91cc[sfind(i)].push_back(i);

92for( int c=0; cc[c].size(); c++)

106 rt = (rt*det(a,cc[c].size()-1))%m;

107}

108return

rt;109

}110

void

work()

124}

125for( int i=2; i<=n; i++)

126if( find(i)!=find(i-1

) )

130 printf( "

%d\n

", ans );

131}

132133

intmain()

139work();

140 }

view code

BZOJ1016 最小生成樹計數

題面描述 最小生成樹計數 給定乙個n個點 m條邊的無向圖,求其最小生成樹的個數。相同邊權的邊不會超過10條。思維難度 提高 難度 提高 題解 先給出兩個引理 1.克魯斯卡爾求最小生成數實際上是分成很多個階段的,你可以感受到 很多邊權相同的邊因為排序順序不同,導致它們被訪問的順序不同。但每處理完乙個邊...

bzoj 1016 最小生成樹計數

首先能發現乙個規律,就是重構最小生成樹的時候,一定不可能用一條權值較大的邊和一條權值較小的邊去替換他們中間的兩條邊。簡而言之,就是只能權值相同的邊相互替換。再進一步說,就是每種權值的邊的數目是一定的。題目上說值相同的邊最多10條,那麼我們可以dfs選哪些,然後用並查集來判斷是否成環。這裡要注意,我們...

BZOJ1016 最小生成樹計數 題解

顯然的是,不同的生成樹的不同權值的邊數一定是一樣的 我們可以先跑一遍kruskal把每種邊需要多少條記錄一下 然後由於相同權值的邊不超過10條,所以在相同權值的邊內部搜一下,看哪些邊加進原圖無環 然後乘法原理和前面的答案乘一下 不同的選擇方法對後面的並查集的更新肯定是沒有影響的,不然一開始的krus...