專題作完了仍是要說兩句留下什麼東西的。編程
矩陣樹定理通俗點講就是: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:
板子。
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 }
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 }
T3:2467生成樹:
Description:
有一種圖形叫作五角形圈。一個五角形圈的中心有1個由n個頂點和n條邊組成的圈。在中心的這個n(n<=100)邊圈的每一條邊同時也是某一個五角形的一條邊,一共有n個不一樣的五角形。這些五角形只在五角形圈的中心的圈上有公共的頂點。如圖0所示是一個4-五角形圈。 如今給定一個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 }
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 }
T5:最小生成樹計數
Description:
最後那個注意是真的要注意。。。
先來兩髮結論:
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 }
T6:輪狀病毒
Description:
輪狀病毒有不少變種,全部輪狀病毒的變種都是從一個輪狀基產生的。一個N輪狀基由圓環上N個不一樣的基原子 和圓心處一個核原子構成的,2個原子之間的邊表示這2個原子之間的信息通道。以下圖所示
N輪狀病毒的產生規律是在一個N輪狀基中刪去若干條邊,使得各原子之間有惟一的信息通道,例如共有16個不 同的3輪狀病毒,以下圖所示
現給定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 }