Prufer序列 生成树定理
Description
在圖論中,樹的定義是連通且無環的無向圖。對于一棵有?nn?個節點且節點從?11?到?nn?編號的樹,它的 Prufer 序列是一個唯一的長為?n?2n?2?的標號序列。 Prufer 序列的構造方法:每次刪除樹中標號最小的葉子節點(即度為?11?的節點),將該點的鄰居加到當前 Prufer 序列的末尾,直到只剩兩個節點為止。
例子:
給定一個?nn?個頂點(從?11?到?nn?標號),?mm?條邊的無向圖?GG(GG?中無重邊或自環)。隨機選擇?GG?的一棵生成樹,計算他的 Prufer 序列的和?SS,重復元素只算一次。 請計算隨機變量?SS?的期望。注意,GG?的生成樹或某棵生成樹的 Prufer 序列都可能不存在,這種情況下,我們認為隨機變量?SS?的值為?00。
為了避免精度問題, 算出實際的期望值乘以圖?GG?的不同生成樹的數目以后的值即可。 這個值可能很大,請輸出它對?109+7109+7?取模以后的值。
Input
每個輸入文件包含多組測試數據。輸入文件的第一行是測試數據組數?TT?(T≤10T≤10)。 對于每組測試數據,第一行是兩個整數?n,mn,m?(3≤n≤100,0≤m≤(n?1)n23≤n≤100,0≤m≤(n?1)n2?),分別是圖的點數和邊數;接下來?mm?行,每行包含兩個整數?u,vu,v(1≤u,v≤n1≤u,v≤n,u≠vu≠v),表示圖中的一條邊。
Output
輸出?TT?行,每行是對應的答案。
Sample Input
1 3 3 1 2 2 3 1 3Sample Output
6題目大意:求所有生成樹的prufer序列和(prufer中有重復序列的只算一次!!!)
題解:
這樣的話,對于每一顆生成樹,我們可以把所有的點全都加進去,然后再減去葉子結點的和。
我們不可能找到所有的生成樹然后一個一個的計算,因此我們用矩陣樹定理來做。
我們先計算圖所有的點的和,并且乘以生成樹的數量,把他們放在sum里。然后再把所有的葉子結點減去,就好了
如果一個葉子節點出現在一顆子樹里,那么把這個點去掉,仍然可一得到圖的該生成樹,而如果這個點是內部節點就不行了。
注意:如果這個葉子節點的度不為1,那么要用這個葉子節點的度數乘以生成樹的數量,才是這個葉子節點對應生成樹的個數。
sum -= 去掉該節點生成樹的數量*該節點的度*該節點的值。
最后得到的sum就是答案
代碼:
#include<iostream>。 #include<cmath> #include <cstring> using namespace std; #define zero(x)((x>0? x:-x)<1e-15) #define int long long int const MAXN = 105; const int mod = 1e9 + 7; int a[MAXN][MAXN]; int b[MAXN][MAXN]; int g[103][103]; int d[105]; int n, m; int det(int a[MAXN][MAXN], int n){ int s=0;for(int i=0; i<n; i++){ int r=i; for(; r<n; r++) // error-prone if(a[r][i]) break; if(r==n+1) return 0; if(r!=i){ s^=1; for(int j=i; j<n; j++) swap(a[i][j], a[r][j]); } for(int j=i+1; j<n; j++){ int x=i, y=j; for(; a[y][i]; ){ // print(a, n); int t=a[y][i]/a[x][i]; if(t){ for(int k=i; k<n; k++){ a[y][k] -= t*a[x][k]%mod; a[y][k] %= mod; } if(a[y][i]==0) break; } swap(x, y); } if(x!=i){ for(int k=i; k<n; ++k) swap(a[x][k], a[y][k]); s ^= 1; } } } int res=1; for(int i=0; i<n; i++) res*=a[i][i], res%=mod; if(s){ res=-res; }if(res < 0) res+=mod; return res; } void prep(int n,int x) {for(int i = 0;i < n;i++){for(int j = 0;j < n;j++){a[i][j] = (i == j)?d[i]-g[i][x]:-g[i][j];}}if(x + 1){for(int i = 0;i < n;i++) a[x][i] = a[i][x] = 0;a[x][x] = 1;} }main() { int cas;scanf("%lld", &cas);while (cas--) {memset(g,0,sizeof(g));memset(d,0,sizeof(d));memset(a,0,sizeof(a));memset(b,0,sizeof(b));scanf("%lld%lld", &n,&m);for(int i = 0;i < m;i++){int u,v;scanf("%lld%lld",&u,&v);u--,v--;d[u]++;d[v]++;g[u][v] = g[v][u] = 1;}prep(n,-1);int sum = det(a,n-1)*((1+n)*n/2) % mod;for(int i = 0;i < n-1;i++){prep(n,i);sum = (sum - (i+1)*d[i] % mod * det(a,n-1) % mod + mod)%mod;//cout<<':'<<sum<<endl;}prep(n,n-1);sum = (sum - n*d[n-1] % mod * det(a,n-2) % mod + mod)%mod;cout<<sum<<endl;}return 0; }
總結
以上是生活随笔為你收集整理的Prufer序列 生成树定理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 李贺的雅号是什么 李贺的雅号是诗鬼
- 下一篇: 罗琦个人资料简介 罗琦是谁