題目描述
給定一個無向圖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 41 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; }