[NOIP模拟赛]偶数度问题

37 篇文章 1 订阅
17 篇文章 0 订阅
题目描述
给定一个无向图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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞模拟的测试数据是指用于评测参选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程序能否正确地解决问题。 在NOI模拟中,测试数据具有以下特: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个选手有相同的机会和条件进行竞争。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值