[NOIP模擬賽]偶數度問題

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章