jzoj1975 【2011集训队出题】连边 (dp)

【2011集训队出题】连边 (Standard IO)

Description

  有N个点(编号1到N)组成的无向图,已经为你连了M条边。请你再连K条边,使得所有的点的度数都是偶数。求有多少种连的方法。要求你连的K条边中不能有重边,但和已经连好的边可以重。不允许自环的存在。求连边的方法数。我们只关心它模10007的余数。

Input

  输入的第一行有三个自然数,分别表示点数N,已经连好的边数M,和你要连的边数K。保证K≤N(N-1)/2
  接下来M行每行两个整数x,y,描述了一条连接x和y的边。
  30%的数据满足:N≤200
  100%的数据满足:N≤1000,M≤N,K≤1000,K≤N(N-1)/2

Output

  输出一个整数,表示连边的方法数模10007的余数

Sample Input

5 1 4
1 2

Sample Output

13

Data Constraint

Hint

【样例说明】
以下是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)}

分析:设f(i,j)表示用i条边,使得j个点的度数为奇数的情况下连边的方法数。注意到所有的状态共用一个N。
f(i,j)的转移是需要一些技巧的。首先,分类讨论第i条边连接的点的度数的奇偶性。如果它连着两个奇数点,那么原来那两个点的度数是偶数,总奇数点个数比现在少2;如果这条边连接的点是一奇一偶,那么奇数点的个数不变。如果连接着两个偶数点,那么原来这两个点都是奇数点,总奇数点的个数比现在多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,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)
答案就是f(K,A)/K!

代码

#include <cstdio>
#define mo 10007
#define N 2005
#define ll long long
using namespace std;

ll jc[N],ny[N],f[N][N];
int d[N];
int n,m,k;

ll ksm(ll x,ll y)
{
    ll base=x,ret=1;
    while (y)
    {
        if (y&1) ret=ret*base%mo;
        base=base*base%mo;
        y/=2;
    }
    return ret;
} 

ll C(ll x){return x*(x-1)/2%mo;}

int main()
{
//  freopen("edges12.in","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    int tot=0;
    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        d[x]++;d[y]++;
    }   
    jc[0]=1;
    for (int i=1;i<=1000;i++)
    {
        jc[i]=jc[i-1]*i%mo;
        ny[i]=ksm(jc[i],mo-2);
    }
    for (int i=1;i<=n;i++) 
        if (!(d[i]%2)) tot++;
    f[0][tot]=1;
    for (int i=1;i<=k;i++)
        for (int j=0;j<=n;j++)
        {
            f[i][j]=f[i-1][j]*j%mo*(n-j)%mo;
            if (j>1) f[i][j]=(f[i][j]+f[i-1][j-2]*C(n-j+2))%mo;
            if (j+2<=n) f[i][j]=(f[i][j]+f[i-1][j+2]*C(j+2))%mo;
            if (i>1) f[i][j]=(f[i][j]-f[i-2][j]*(i-1)%mo*(C(n)-i+2)%mo+mo)%mo;
        }
    f[k][n]=f[k][n]*ny[k]%mo;
    printf("%lld",f[k][n]);
}

猜你喜欢

转载自blog.csdn.net/zhanghaoxian1/article/details/79264877