题目描述
给定一个无向图G,有N个节点,节点编号为1...N。图G中已经连了M条边。请你再连接K条边,使得所有的节点的度数都是偶数。要求你再连接K条边,使得所有的节点的度数都是偶数。
求有多少种连的方法。要求你连的K条边中不能有重边,但和已经连好的M条边可以重。不允许自环的存在。求连边的方法数。
输入格式
第1行:3个整数,分别表示N(N≤1000),M(M≤N),K(K≤1000,K≤N*(N-1)/2)。
接下来M行每行2个整数x,y,描述了一条已经连接好的x和y的边
输出格式
第1行:1个整数,表示连边的方法数。答案模10^9+7
输入样例
5 1 4
1 2
输出样例
13
样例说明
以下是13种两边的方法(只显示新连的边):
{ (1,2),(1,3),(1,4),(3,4) }
{ (1,2),(1,3),(1,5),(3,5) }
{ (1,2),(1,4),(1,5),(4,5) }
{ (1,2),(2,3),(2,4),(3,4) }
{ (1,2),(2,3),(2,5),(3,5) }
{ (1,2),(2,4),(2,5),(4,5) }
{ (1,2),(3,4),(3,5),(4,5) }
{ (1,3),(2,4),(3,5),(4,5) }
{ (1,3),(2,5),(3,4),(4,5) }
{ (1,4),(2,3),(3,5),(4,5) }
{ (1,4),(2,5),(3,4),(3,5) }
{ (1,5),(2,3),(3,4),(4,5) }
{ (1,5),(2,4),(3,4),(3,5) }
题解
首先,已经连好的M条边并没有过多的意义。对于已经连好的点,只关心它们的度数的奇偶性。可以假设连好的边使得前P个点的度数是奇数。这样问题可以转化为:从一个空白的无向图开始,有多少种连边的方法,使得前P个点的度数是奇数。
设f[i][j]表示用i条边,使得j个点的度数为奇数的情况下连边的方法数。
首先分类讨论第i条边连接的点的度数的奇偶性。
①如果连接的点是一奇一偶,那么奇数点的个数不变:f[i][j]=f[i-1][j]*(N-j)*j
②如果连接两个奇数点,那么原来两个点的度数是偶数:f[i][j]+=f[i−1][j−2]*C(j,2)
③如果连接两个偶数点,那么原来两个点的度数是奇数:f[i][j]+=f[i−1][j+2]*C(N−j,2)
但这样转移的话无法保证没有重边。于是考虑第i条边和之前的第x条边重复的情况。x有i-1种取值。删去第i条和第x条边,所有的点的度数的奇偶性不变:
f[i][j]-=f[i-2][j]*(i-1)*( C(N,2)-(i-2) )
总的转移方程是:
f[i][j]=f[i-1][j]*(N-j)*j + f[i-1][j-2]*C(j,2) + f[i-1][j+2]*C(N-j,2) - f[i-2][j]*(i-1)*( C(N,2)-(i-2) )再去掉重复的方案数,答案为f[K][P]/K!。
#include<cstring> #include<cstdio> typedef long long LL; const int INF=0x3f3f3f3f; const int MOD=1e9+7; const int N=1005; LL n, m, k, w, sum=1; LL dp[N][N], esum, psum; bool pot[N]; //设f[k][p]表示用k条边,使得p个点的度数为奇数的情况下连边的方法数。 //f[k][p]=f[k-1][p]*(n-p)*p + f[k-1][p-2]*C(p,2) + f[k-1][p+2]*C(n-p,2) - f[k-2][p]*(k-1)*( C(n,2)-(k-2) ) LL DFS( LL k, LL p ) { if( dp[k][p]!=-1 ) return dp[k][p]; if( !k ) return dp[k][p]=(p==0); if( k+k<p || p>n ) return dp[k][p]=0; LL ret=0; if( n-p>=2 ) ( ret+=DFS( k-1, p+2 )*(n-p)*(n-p-1)>>1%MOD )%=MOD; if( n-p && p ) ( ret+=DFS( k-1, p )*(n-p)*p%MOD )%=MOD; if( p>=2 ) ( ret+=DFS( k-1, p-2 )*p*(p-1)>>1%MOD )%=MOD; if( k>=2 ) ( ret-=DFS( k-2, p )*(esum-k+2)*(k-1)%MOD )%=MOD; ret=( ret%MOD+MOD )%MOD; return dp[k][p]=ret; } LL Exeu(LL a,LL b,LL c) { if (!a)return -1; if ( !(b%a) )return b/a; return ( c*Exeu( c%a, a-b%a, a )+b )/a; } /* ax=b%c ax=cy+b cy=a-b%a y=Exeu( c%a, (b?a-b%a:0), a ) x=(c*y+b)/a */ int main() { scanf( "%I64d%I64d%I64d", &n, &m, &k ); esum=n*(n-1)>>1%MOD; for( int i=1; i<=m; i++ ) { int x, y; scanf( "%d%d", &x, &y ); pot[x]^=1; pot[y]^=1; } for( int i=1; i<=n; i++ ) psum+=pot[i]; memset( dp, -1, sizeof dp ); w=DFS( k, psum ); for( LL i=1; i<=k; i++ ) sum=sum*i%MOD; w=Exeu( sum, w, MOD ); printf( "%I64d\n", w ); return 0; }