[專題總結]矩陣樹定理Matrix_Tree及題目&題解

專題作完了仍是要說兩句留下什麼東西的。編程

矩陣樹定理通俗點講就是:ide

創建矩陣A[i][j]=edge(i,j),(i!=j)。即矩陣這一項的係數是兩點間直接相連的邊數。ui

而A[i][i]=deg(i)。即對角線上都是這個點的度數。spa

獲得這個矩陣後,隨便刪掉一行一列後進行高斯消元獲得上三角矩陣,對角線上值的積就是生成樹的個數。(就是行列式)3d

順便提一下行列式的性質:code

交換兩行/列,行列式的值變爲相反數。blog

一行的每一項減去另外一行的若干倍,行列式不變。ip

一行的每一項都乘一個常數,行列式也乘這個常數。it

到這裏就夠作題了。pip

 

T1:小Z的房間:

Descpiption:

你忽然有了一個大房子,房子裏面有一些房間。事實上,你的房子能夠看作是一個包含n*m個格子的格狀矩形,每一個格子是一個房間或者是一個柱子。在一開始的時候,相鄰的格子之間都有牆隔着。(n,m<=10)

你想要打通一些相鄰房間的牆,使得全部房間可以互相到達。在此過程當中,你不能把房子給打穿,或者打通柱子(以及柱子旁邊的牆)。同時,你不但願在房子中有小偷的時候會很難抓,因此你但願任意兩個房間之間都只有一條通路。如今,你但願統計一共有多少種可行的方案。Mod 10^9

板子。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define mod 1000000000
 4 int ord[11][11],n,m,tim,A[101][101];char s[11][11];
 5 void link(int a,int b){A[a][a]++;A[b][b]++;A[a][b]--;A[b][a]--;}
 6 int Gauss(int ans=1){
 7     for(int i=1;i<=tim;++i)for(int j=i+1;j<=tim;++j)while(A[j][i]){
 8         int d=A[i][i]/A[j][i];for(int k=i;k<=tim;++k)A[i][k]=(A[i][k]-1ll*d*A[j][k]%mod+mod)%mod;
 9         std::swap(A[i],A[j]);ans*=-1;
10     }for(int i=1;i<=tim;++i)ans=1ll*ans*A[i][i]%mod;return (ans+mod)%mod;
11 }
12 int main(){
13     scanf("%d%d",&n,&m);
14     for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
15     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(s[i][j]=='.')ord[i][j]=++tim;
16     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(s[i][j]=='.'){
17         if(s[i-1][j]=='.')link(ord[i-1][j],ord[i][j]);
18         if(s[i][j-1]=='.')link(ord[i][j-1],ord[i][j]);
19     }
20     tim--;printf("%d\n",Gauss());
21 }
View Code

 

 

T2:重建:

Description:

T 國有n(n<=50)個城市,用若干雙向道路鏈接。一對城市之間至多存在一條道路。
在一次洪水以後,一些道路受損沒法通行。雖然已經有人開始調查道路的損毀狀況,但直到如今幾乎沒有消息傳回。
幸運的是,此前 T 國政府調查過每條道路的強度,如今他們但願只利用這些信息估計災情。
具體地,給定每條道路在洪水後仍能通行的機率,請計算仍能通行的道路恰有N-1條,且能聯通全部城市的機率。

spj:相對偏差<1e-4

變元的了。

就是邊帶權,要求出全部生成樹邊權積的和。

那麼統計度數和邊權的時候都再也不++,--,按照邊權來就能夠了。

可是這道題求的沒有這麼簡單。

求的是剛好造成一棵樹,不能有多邊。即求$\sum\limits_{tree} \prod\limits_{i \in tree}w_i \prod\limits_{i \notin tree}(1-w_i)$

不在樹裏的很差處理,因此化一下獲得$\sum\limits_{tree} \prod\limits_{i \in tree}\frac{w_i}{1-w_i} \prod\limits_i(1-w_i)$

而後就獲得了新的邊權。

要注意設個eps,當$1-w_i$很小時除法容易鍋,若是它小於eps就把它強制置爲eps。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cmath>
 4 using namespace std;
 5 long double A[55][55];int n;
 6 long double Gauss(){long double ans=1;
 7     for(int i=1;i<n;++i){
 8         long double mx=0;int pt;
 9         for(int j=i;j<n;++j)if(fabs(A[j][i])>mx)pt=j,mx=fabs(A[j][i]);
10         if(pt!=i)swap(A[pt],A[i]);
11         for(int j=i+1;j<n;++j){
12             long double rate=A[j][i]/A[i][i];
13             for(int k=i;k<=n;++k)A[j][k]-=rate*A[i][k];
14         }
15     }
16     for(int i=1;i<n;++i)ans*=A[i][i];return ans;
17 }
18 int main(){
19     scanf("%d",&n);long double pans=1;
20     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)scanf("%Lf",&A[i][j]);
21     for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)pans*=1.0L-A[i][j]<1e-9?1e-9:1.0L-A[i][j];
22     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)A[i][j]/=1.0L-A[i][j]<1e-9?1e-9:1.0L-A[i][j];
23     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(i!=j)A[i][i]+=A[i][j];
24     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(i!=j)A[i][j]=-A[i][j];
25     printf("%.10Lf\n",Gauss()*pans);
26 }
View Code

 

 

T3:2467生成樹:

Description:

有一種圖形叫作五角形圈。一個五角形圈的中心有1個由n個頂點和n條邊組成的圈。在中心的這個n(n<=100)邊圈的每一條邊同時也是某一個五角形的一條邊,一共有n個不一樣的五角形。這些五角形只在五角形圈的中心的圈上有公共的頂點。如圖0所示是一個4-五角形圈。 image 如今給定一個n五角形圈,你的任務就是求出n五角形圈的不一樣生成樹的數目。還記得什麼是圖的生成樹嗎?一個圖的生成樹是保留原圖的全部頂點以及頂點的數目減去一這麼多條邊,從而生成的一棵樹。 注意:在給定的n五角形圈中全部頂點均視爲不一樣的頂點。mod2007

板子。注意判斷n=2

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define mod 2007
 4 int A[404][404],n;
 5 int Gauss(int ans=1){n--;
 6     for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)while(A[j][i]){
 7         int d=A[i][i]/A[j][i];for(int k=i;k<=n;++k)A[i][k]=(A[i][k]+mod-A[j][k]*d%mod)%mod;
 8         std::swap(A[i],A[j]);ans*=-1;
 9     }for(int i=1;i<=n;++i)ans=ans*A[i][i]%mod;return (ans+mod)%mod;
10 }
11 int main(){
12     int t;scanf("%d",&t);
13     while(t--){
14         scanf("%d",&n);n<<=2;
15         for(int i=1;i<n;++i)for(int j=1;j<n;++j)A[i][j]=0;
16         for(int i=1;i<n;++i)A[i][i]=i%4==1?4:2;
17         for(int i=1;i<n;++i)A[i][i+1]--,A[i+1][i]--;
18         A[1][n-3]--,A[n-3][1]--;
19         for(int i=1;i<n;i+=4)A[i][i+4]--,A[i+4][i]--;
20         printf("%d\n",Gauss());
21     }
22 }
View Code

 

 

T4:黑暗前的幻想鄉

Description:

四年一度的幻想鄉大選開始了,最近幻想鄉最大的問題是不少來歷不明的妖怪涌入了幻想鄉,擾亂了幻想鄉昔日的秩序。可是幻想鄉的建制派妖怪(人類)博麗靈夢和八雲紫等人整日高談全部妖怪平等,幻想鄉多元化等等,對於幻想鄉目前面臨的種種大問題卻給不出合理的解決方案。

風見幽香是幻想鄉里少有的意識到了問題嚴重性的大妖怪。她此次勇敢地站了出來參加幻想鄉大選,提出包括在幻想鄉邊境建牆(並讓人類出錢),大力開展基礎設施建設挽回失業率等一系列方案,成爲了大選年出人意料的黑馬並順利地當上了幻想鄉的大統領。

幽香上臺之後,第一項措施就是要修建幻想鄉的公路。幻想鄉一共有n(n<=17)個城市,以前原來沒有任何路。幽香向選民承諾要減稅,因此她打算只修n-1條公路將這些城市鏈接起來。可是幻想鄉有正n-1個建築公司,每一個建築公司都想在修路地過程當中得到一些好處。雖然這些建築公司在選舉前沒有給幽香錢,幽香仍是打算和他們搞好關係,由於她還期望他們幫她建牆。因此她打算讓每一個建築公司都負責一條路來修。

每一個建築公司都告訴了幽香本身有能力負責修建的路是哪些城市之間的。因此幽香打算n-1條可以鏈接幻想鄉全部城市的邊,而後每條邊都交給一個可以負責該邊的建築公司修建,而且每一個建築公司都剛好修建一條邊。

幽香如今想要知道一共有多少種可能的方案呢?兩個方案不一樣當且僅當它們要麼修的邊的集合不一樣,要麼邊的分配方式不一樣。mod1e9+7

數據範圍很小,考慮枚舉子集進行容斥。

奇加偶減,而後就是板子了。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define mod 1000000007
 4 int n,A[18][18],c[18][18][18],ans;
 5 int Gauss(){int ans=1;
 6     for(int i=1;i<n;++i)for(int j=i+1;j<n;++j)while(A[j][i]){
 7         int d=A[i][i]/A[j][i];for(int k=i;k<n;++k)A[i][k]=(A[i][k]-1ll*A[j][k]*d%mod+mod)%mod;
 8         std::swap(A[i],A[j]);ans*=-1;
 9     }for(int i=1;i<n;++i)ans=1ll*ans*A[i][i]%mod;return ans;
10 }
11 int main(){
12     scanf("%d",&n);
13     for(int i=1;i<n;++i){
14         int k,x,y;scanf("%d",&k);
15         while(k--)scanf("%d%d",&x,&y),c[i][x][x]++,c[i][y][y]++,c[i][x][y]--,c[i][y][x]--;
16     }
17     for(int st=1;st<1<<n-1;++st){
18         for(int i=1;i<n;++i)for(int j=1;j<n;++j)A[i][j]=0;
19         for(int l=1;l<n;++l)if(st&1<<l-1)
20             for(int i=1;i<n;++i)for(int j=1;j<n;++j)A[i][j]+=c[l][i][j];
21         int pt=1;for(int i=st;i;i^=i&-i)pt*=-1;if(n&1^1)pt*=-1;//printf("%d %d\n",st,pt);
22         ans=(0ll+ans+mod+pt*Gauss())%mod;//printf("%d\n",ans);
23     }printf("%d\n",ans);
24 }
View Code

 

T5:最小生成樹計數

Description:

原題來自:JSOI 2008

如今給出了一個簡單無向加權圖。你不知足於求出這個圖的最小生成樹,而但願知道這個圖中有多少個不一樣的最小生成樹。mod31011(若是兩顆最小生成樹中至少有一條邊不一樣,則這兩個最小生成樹就是不一樣的)。對於所有數據,n<=100,m<=1000

數據保證不會出現自回邊和重邊。

注意:具備相同權值的邊不會超過10條。

最後那個注意是真的要注意。。。

先來兩髮結論:

1。在不一樣的最小生成樹中,每種權值的邊用的數量是必定的。

2。在不一樣的最小生成樹中,每種權值的邊都加入完後,圖的聯通性是必定的。

(即如,你用並查集維護的話,若是每次合併都把小編號的當成根,那麼獲得並查集都徹底同樣)

仔細想想,其實都不難證。

而後你只要狀壓每種邊權的10條邊用了哪些就行了。

也能夠用matrix_tree作。可是我打的搜索。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<unordered_map>
 4 #include<vector>
 5 using namespace std;
 6 unordered_map<int,int>M;
 7 struct edge{
 8     int a,b,v;
 9     friend bool operator<(edge A,edge B){
10         return A.v<B.v;
11     }
12 }E[1005];
13 vector<edge>v[1005];
14 int n,m,f[105],cnt,uc[1005],p[1005][105],ans=1;
15 int find(int k){return f[k]==k?k:f[k]=find(f[k]);}
16 void merge(int a,int b){if(a<b)f[b]=a;else f[a]=b;}
17 int main(){
18     scanf("%d%d",&n,&m);
19     for(int i=1;i<=n;++i)f[i]=i;
20     for(int i=1;i<=m;++i)scanf("%d%d%d",&E[i].a,&E[i].b,&E[i].v);
21     sort(E+1,E+1+m);
22     for(int i=1;i<=m;++i)if(M[E[i].v]!=cnt||cnt==0)M[E[i].v]=++cnt;
23     for(int i=1;i<=m;++i)E[i].v=M[E[i].v];
24     for(int i=1;i<=m;++i)v[E[i].v].push_back(E[i]);
25     for(int i=1;i<=cnt;++i){
26         for(int j=0;j<v[i].size();++j)if(find(v[i][j].a)!=find(v[i][j].b))
27             merge(f[v[i][j].a],f[v[i][j].b]),uc[i]++;
28         for(int j=1;j<=n;++j)p[i][j]=find(j);
29     }
30     for(int i=1;i<=n;++i)if(find(i)!=1){puts("0");return 0;}
31     for(int i=1;i<=n;++i)p[0][i]=i;
32     for(int i=1;i<=cnt;++i){
33         int pl=0;//printf("%d %d\n",i,pl);
34         for(int j=0;j<1<<v[i].size();++j){
35             for(int l=1;l<=n;++l)f[l]=p[i-1][l];
36             for(int l=0;l<v[i].size();++l)if(j&1<<l)
37                 if(find(v[i][l].a)==find(v[i][l].b))goto B;
38                 else merge(f[v[i][l].a],f[v[i][l].b]);
39             for(int l=1;l<=n;++l)if(find(l)!=p[i][l])goto B;
40             pl++;B:;
41         }
42         ans=ans*pl%31011;//printf("%d %d\n",i,pl);
43     }printf("%d\n",ans);
44 }
View Code

 

T6:輪狀病毒

Description:

輪狀病毒有不少變種,全部輪狀病毒的變種都是從一個輪狀基產生的。一個N輪狀基由圓環上N個不一樣的基原子 和圓心處一個核原子構成的,2個原子之間的邊表示這2個原子之間的信息通道。以下圖所示

image

N輪狀病毒的產生規律是在一個N輪狀基中刪去若干條邊,使得各原子之間有惟一的信息通道,例如共有16個不 同的3輪狀病毒,以下圖所示

image

現給定n(N<=100),編程計算有多少個不一樣的n輪狀病毒

感受少了點什麼。

對,你沒看錯,沒有模數。須要高精,__int128卡不過的。

那麼直接跑Gauss得T飛吧。

因此正確的姿式是,小點matrix_tree打表找規律。大點dp寫高精。

給出dp式子$dp[i]=dp[i-1]\times 3 - dp[i-2] +2$

而後就打個高精就行。

 1 #include<cstdio>
 2 int n;
 3 struct bigint{
 4     #define mod 100000000
 5     int a[9];
 6     friend void operator+=(bigint &a,bigint b){
 7         for(int i=8;~i;--i)a.a[i]+=b.a[i];
 8         for(int i=0;i<=8;++i)if(a.a[i]>=mod)a.a[i]-=mod,a.a[i+1]++;
 9     }
10     friend void operator-=(bigint &a,bigint b){
11         for(int i=8;~i;--i)a.a[i]-=b.a[i];
12         for(int i=0;i<=8;++i)if(a.a[i]<0)a.a[i]+=mod,a.a[i+1]--;
13     }
14     void print(int i=8){
15         for(;~i;--i)if(a[i]){printf("%d",a[i]);break;}
16         for(--i;~i;--i)printf("%08d",a[i]);puts("");
17     }
18 }ldp,nw,nxt;
19 int main(){
20     nw.a[0]=1;int n;scanf("%d",&n);n--;
21     while(n--){
22         nxt=nw;nxt+=nw;nxt+=nw;nxt-=ldp;nxt.a[0]+=2;
23         ldp=nw;nw=nxt;
24     }nw.print();
25 }
View Code
相關文章
相關標籤/搜索