1. 程式人生 > >CSU 1805 Three Capitals(矩陣樹定理+Best定理)

CSU 1805 Three Capitals(矩陣樹定理+Best定理)

mod std div air vector 一定的 選擇 cstring type

http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1805

題意:

A和B之間有a條邊,A和G之間有b條邊,B和G之間有c條邊。現在從A點出發走遍所有的邊,然後再回到A點,問一共有多少種方法。

思路:

16年湖南省賽題目,這道題目是求歐拉回路的個數,和生成樹的計數有一定的聯系。

首先給出神奇的Best定理,這是什麽鬼定理,反正查不到什麽有關該定理的文章。。。

$ec(G)=t_s(G)\cdot deg(s)! \cdot \prod_{v\in V,\ v\ne s} (deg(v)-1)!,\ t_s(G):=$以s為根的外向樹的個數。

這個有向圖的外向樹個數其實和無向圖的生成樹個數是差不多的,總之就是矩陣樹定理,但是稍微還是有點不同的地方。

基爾霍夫矩陣的構造是不太一樣的,畢竟一個是無向圖,一個是有向圖:

無向圖中的度數矩陣是每個頂點的度數,有向圖中的度數矩陣是每個頂點的入度。

鄰接矩陣的話是u->v的邊數,這和無向圖是差不多的。

然後是矩陣的計算:

無向圖的生成樹個數=任意n-1階主子式的值

有向圖的外向樹個數=除去根節點所在的階的主子式的值

註意一下,這道題目還需要計算組合數,比如說,A和B之間有a條邊,那麽我可以選擇x條邊為入邊,那麽剩余的a-x條邊為出邊,在這個過程中就會有不同的選擇方法。總的也就是$C(a,x)*C(b,y)*C(c,z)$。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<sstream>
 6 #include<vector>
 7 #include<stack>
 8 #include<queue>
 9 #include<cmath>
10 #include<map>
11 #include<set>
12
using namespace std; 13 typedef long long ll; 14 typedef pair<int,ll> pll; 15 const int INF = 0x3f3f3f3f; 16 const int maxn=500+5; 17 const ll mod=1e9+7; 18 19 int a,b,c; 20 ll f[100005]; 21 ll A[5][5]; 22 23 void init() 24 { 25 f[0]=1; 26 for(int i=1;i<=100005;i++) f[i]=f[i-1]*i%mod; 27 } 28 29 ll qpow(ll a,ll n) 30 { 31 ll ans=1; 32 while(n) 33 { 34 if(n&1) ans=ans*a%mod; 35 a=a*a%mod; 36 n>>=1; 37 } 38 return ans; 39 } 40 41 ll C(ll n,ll m) 42 { 43 return (f[n]*qpow(f[m],mod-2)%mod)*qpow(f[n-m],mod-2)%mod; 44 } 45 46 ll calc() 47 { 48 return (A[1][1]*A[2][2]%mod-A[1][2]*A[2][1]%mod+mod)%mod; 49 } 50 51 int main() 52 { 53 //freopen("in.txt","r",stdin); 54 init(); 55 while(~scanf("%d%d%d",&a,&b,&c)) 56 { 57 if((a+c)&1 || (a+b)&1 || (b+c)&1) {puts("0");continue;} 58 ll ans=0; 59 for(int i=0;i<=a;i++) //枚舉A->B的邊數 60 { 61 memset(A,0,sizeof(A)); 62 A[0][0]=(a+b)/2; 63 A[1][1]=(a+c)/2; 64 A[2][2]=(b+c)/2; 65 A[0][1]=-i; 66 A[1][0]=-(a-i); 67 A[0][2]=-(A[0][0]-i); 68 A[2][0]=-(b+A[0][2]); 69 A[1][2]=-(A[1][1]+A[1][0]); 70 A[2][1]=-(c+A[1][2]); 71 if(A[0][2]>0 || A[2][0]>0 || A[1][2]>0 || A[2][1]>0) continue; 72 73 ll res=(C(a,i)*C(c,-A[1][2])%mod)*C(b,-A[0][2])%mod; 74 75 res=(res*calc())%mod; 76 for(int i=1;i<3;i++) res=res*f[A[i][i]-1]%mod; 77 res=res*f[A[0][0]]%mod; 78 ans=(ans+res)%mod; 79 } 80 printf("%lld\n",ans); 81 } 82 return 0; 83 }

CSU 1805 Three Capitals(矩陣樹定理+Best定理)