1805: Three Capitals
Time Limit: 5 Sec Memory Limit: 128 Mb Submitted: 51 Solved: 32
Description
Input
Output
Sample Input
1 1 3 1 3 1 100000 100000 100000
Sample Output
12 24 525502296
Hint
Source
湖南省第十二届大学生计算机程序设计竞赛
关于矩阵树定理,在暑假培训的时候hc学长略微提到过,没想到就是去年省赛的题目……
所谓矩阵树定理,就是指一个图的生成树个数,等于基尔霍夫矩阵的任意n-1阶主子式的行列式的值。而基尔霍夫矩阵K又两个部分构成,一是图的邻接矩阵A,另一个是每个节点的度数矩阵B,如此基尔霍夫矩阵K=B-A。具体证明我就不给出了,自己去查吧,其实我也不知道……
然后本题有点不同,这题是求欧拉回路的条数。其实差不多,对于欧拉回路的条数,我们有一个Best定理与Matrix-Tree定理类似,不同之处在于Best定理里面,度数矩阵B的度数是节点的入度,然后计算行列式的时候主子式是去掉起点所在行列的主子式,最后结果还要乘以每个节点入度减一的阶乘(起点不用减一)。
知道了公式之后套就行了,但是还要注意,这题是无向图,所以我们还要枚举哪些边作为出边,哪些边作为入边。由于本题是一个三角形的环,所以只要枚举条边的出入情况就可以推出其他边的情况,所以时间复杂度上可以接受。我们假设枚举之后,a、b、c三条边的出边个数分别为x、y、z,那么最后结果还要乘上组合数C(a,x)*C(b,y)*C(c,z)。具体见代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define LL long long
#define mod 1000000007
using namespace std;
LL fac[100010],x[3][3];
int a,b,c;
void init()
{
fac[0]=1;
for(int i=1;i<=100005;i++) fac[i]=fac[i-1]*i%mod;
}
LL qpow(LL a,LL n)
{
LL ans=1;
while(n)
{
if(n&1) ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
LL C(LL n,LL m)
{
return fac[n]*qpow(fac[m]*fac[n-m]%mod,mod-2)%mod;
}
int main()
{
init();
while(~scanf("%d%d%d",&a,&b,&c))
{
LL ans=0;
if((a+c)&1||(a+b)&1||(b+c)&1) {puts("0");continue;}
for(int i=0;i<=a;i++)
{
memset(x,0,sizeof(x));
x[0][0]=(a+b)/2; x[1][1]=(a+c)/2; x[2][2]=(b+c)/2;
x[0][1]=-i; x[1][0]=-(a-i); x[0][2]=-(x[0][0]-i);
x[2][0]=-(b+x[0][2]); x[1][2]=-(x[1][1]+x[1][0]); x[2][1]=-(x[2][2]+x[2][0]);
if (x[2][0]>0||x[1][2]>0||x[0][2]>0||x[2][1]>0) continue;
LL res=(C(a,-x[0][1])*C(b,-x[0][2])%mod)*C(c,-x[1][2])%mod;
res=res*fac[x[1][1]-1]%mod*fac[x[2][2]-1]%mod*fac[x[0][0]]%mod;
res=res*((x[1][1]*x[2][2]%mod-x[1][2]*x[2][1]%mod+mod)%mod)%mod; //计算行列式
ans+=res;
}
printf("%lld\n",ans%mod);
}
}