• [BZOJ2169] 连边


    问题描述

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

    输入格式

    输入的第一行有三个自然数,分别表示点数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

    输出格式

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

    样例输入

    5 1 4
    1 2

    样例输出

    13

    说明

    对于20%的数据, 4≤N≤100。对于40%的数据, 4≤N≤5000。对于100%的数据,4≤N≤50000 1≤M≤10 M≤N 所有出现的整数均不超过32位含符号整数。 时间限制:1s

    链接

    BZOJ

    解析

    考虑使用动态规划的方法来实现。设(f[i][j])表示当前加了(i)条边,还有(j)个点的度数为奇数。那么,考虑如何转移。每连一条边,我们就要在(n)个点中选择两个点。我们可以选择两个奇点,使奇点数减2。也可以选择偶点,使奇点数加2,也可以选择一奇一偶,则奇点个数不变。所以,我们有如下状态转移方程:

    [f[i][j]=f[i-1][j-2] imes C_{n-j+2}^2+f[i-1][j+2] imes C_{j+2}^2+f[i-1][j] imes C_j^2 imes C_{n-j+1}^2 ]

    但是,这样计数是会有重复的。一是连的边中会有重边,所以,我们需要减去上一层转移中有重边的方案。我们可以强制有两条边连在了同两个点上,其余边都不重复,即

    [f[i][j]=f[i][j]-f[i-2][j] imes (C_n^2-i+2) ]

    另外一个是连边没有顺序问题,所以

    [f[i][j]=f[i][j]/i ]

    代码

    #include <iostream>
    #include <cstdio>
    #define N 1002
    using namespace std;
    const int mod=10007;
    int n,m,k,i,j,d[N],f[N][N],C[N][N];
    int read()
    {
        char c=getchar();
        int w=0;
        while(c<'0'||c>'9') c=getchar();
        while(c<='9'&&c>='0'){
            w=w*10+c-'0';
            c=getchar();
        }
        return w;
    }
    int poww(int a,int b)
    {
        int ans=1,base=a;
        while(b){
            if(b&1) ans=ans*base%mod;
            base=base*base%mod;
            b>>=1;
        }
        return ans;
    }
    int main()
    {
        n=read();m=read();k=read();
        for(i=1;i<=m;i++){
            int u=read(),v=read();
            d[u]++;d[v]++;
        }
        C[0][0]=1;
        for(i=1;i<=n;i++){
            C[i][0]=1;
            for(j=1;j<=n;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
        int cnt=0;
        for(i=1;i<=n;i++){
            if(d[i]%2) cnt++;
        }
        f[0][cnt]=1;
        for(i=1;i<=k;i++){
            for(j=0;j<=n;j++){
                if(j>=2) f[i][j]=(f[i][j]+f[i-1][j-2]*C[n-j+2][2]%mod)%mod;
                if(j<=n-2) f[i][j]=(f[i][j]+f[i-1][j+2]*C[j+2][2]%mod)%mod;
                f[i][j]=(f[i][j]+f[i-1][j]*C[j][1]%mod*C[n-j][1]%mod)%mod;
                if(i>=2) f[i][j]=(f[i][j]-f[i-2][j]*(C[n][2]-i+2+mod)%mod+mod)%mod;
                f[i][j]=f[i][j]*poww(i,mod-2)%mod;
            }
        }
        printf("%d
    ",f[k][0]);
        return 0;
    }
    
  • 相关阅读:
    WM_COMMAND 和 WM_NOTIFY 的区别
    C 游戏所要看的书
    Source Insight中文字体设置
    在 windows7 中使用 vs2003 时,“在文件中查找”导致无响应的问题
    解决VS2008 调试启动特别慢
    c++ 浅谈 new 的使用
    Access界面基础操作
    与孩子一起学编程12章
    YT工作日志-0911
    两种方式遍历二叉树--递归方式和非递归方式
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12220718.html
Copyright © 2020-2023  润新知