bzoj1016 JSOI2008 最小生成樹計數

2021-08-19 15:31:46 字數 2486 閱讀 7382

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,0

00。資料保證不會出現自回邊和重邊。注意:具有相同權值的邊不會超過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 這篇文章講的real清楚 下面是引用的原文

首先需要乙個結論,對於乙個圖的不同最小生成樹,每種方案所包含的每種權值的邊的數量一定一致。換句話說,把每種方案包含的所有邊的邊權都寫下來,寫出來的序列一定都一樣。關於這個結論的說明放在最後。

這樣的話,可以先做一遍kruskal,記下每種邊權的使用次數,然後對於每種邊權進行dfs,判斷有多少種合法的組合方式【一種方案合法意味著:1.加入每條邊時,邊的兩端點一定屬於不同的並查集,也就是仍然要符合kruskal的要求。2.加入的總邊數等於開始統計的使用次數。第一條要求也就決定了不能用組合數進行計算,只能dfs,而因為相同邊權的邊不超過10,再加上一些最簡單的剪枝,執行速度很快。】,然後用乘法原理即可。

注意:

1.dfs的時候要回溯,所以這裡的並查集不能進行路徑壓縮。

2.處理完每種邊之後要把這些邊加上去,這才是kruskal的過程。

3.考慮圖不連通,即不存在最小生成樹的情況。

最後說一下原理。考慮kruskal的過程,只有當這一權值的邊全部考慮之後才會考慮權值比他大的邊。舉個最簡單的例子,假設有兩種方案,第一種方案有邊x1,x4,第二種有邊x2,x3,且x1< x2< x3< x4。因為一條邊只能連線兩個連通塊,那麼x2,x3中一定有乙個能起到x4的作用,那麼這個能起作用的點和x1組成的方案才是最小生成樹。

既然這樣,不同的方案從何而來呢?來自於相同的權值的邊,在排序後的順序不同,而他們又起著相同的作用(也就是連線相同的聯通塊)【因為如果作用不一樣的話,他們應該都被納入方案】,那麼先考慮這個和先考慮那個就會得出不同的方案。

於是,我們對每一組權值相同的邊,知道了他們總的使用次數以後,進行暴力的列舉統計方案數。因為我們知道,不管怎麼選,最後的結果,也就是給後面帶來的影響,都是相同的【因為如果影響不同,那麼就不應該現在從中挑選,當初kruskal的時候應該都選進來。】

那麼這種做法,和直接暴力列舉的區別就顯現了。暴力列舉是整個的指數級,而這種做法是把總數分成很多小塊,在每一塊裡暴力列舉,最後的複雜度是每一塊的指數相加,自然小了很多。

相當於我先用kruskal做出我的最小生成樹 然後記錄下一共使用了多少種權值

因為我是排好序的所以我給每個權值分段 並且記錄下我每種權值都用了多少

然後根據上面的證明 我權值連線的應該都是相同的聯通塊才行

每次做完一種權值後就給他們連起來 (ps:造成的效果是一樣的)所以隨便連就可以了

#include

#include

#define mod 31011

#define n 110

#define m 1100

using namespace std;

inline int read()

return x;

}struct nodedata[m];

struct node1group[m];

inline bool cmp(node a,node b)

void dfs(int id,int step,int now)

dfs(id,step+1,now);

int xx=find(data[step].x),yy=find(data[step].y);

if (xx!=yy)

}int main()

sort(data+1,data+m+1,cmp);int g=0,cnt=0;

for (int i=1;i<=n;++i) fa[i]=i;

for (int i=1;i<=m;++i)

}group[g].r=m;

if (cnt1)

int ans=1;

for (int i=1;i<=n;++i) fa[i]=i;

for (int i=1;i<=g;++i)

}printf("%d\n",ans);

return

0;}

bzoj1016 JSOI2008 最小生成樹計數

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

bzoj 1016 kruscal 乘法原理

題意 求n個點 m條邊的不同的最小生成樹的方案數 每種邊權的邊數量固定 作用固定 先做一遍最小生成樹,求出每種邊權在最小生成樹中的數量num i 再從小到大對每種邊權進行dfs,求出對於第i種邊權,有多少種滿足num i 的取法 根據乘法原理乘上即可 對於已經處理完的第i種邊權,把該種邊權所有的邊能...

1016 JSOI2008 最小生成樹計數

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