CSU 1805 Three Capitals(矩阵树定理+Best定理)
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 }