BZOJ 2169 连边 DP

思路:DP

提交:\(1\)次(课上刚讲过)

题解:

如果不管重边的话,我们设\(f[i][j]\)表示连了\(i\)条边,\(j\)个点的度数是奇数的方案数,那么显然我们可以分三种状态转移:
\(f[i][j]+=f[i-1][j-2]*C_{n-j+2}^2;\)连了两个偶点
\(f[i][j]+=f[i-1][j]*j*(n-j);\)连了一奇一偶
\(f[i][j]+=f[i-1][j+2]*C_{j+2}^2;\)连了两个奇点
考虑如何处理重边:我们设\(f[i][j]\)表示连了\(i\)条边,\(j\)个点的度数是奇数,并且没有重边方案数,那么除了上面的转移,还要多一个:
\(f[i][j]-=f[i-2][j]*(C_n^2-(i-2))\),含义是任选一条边,假设被连了两次,那就相当于他没有连(不改变奇偶点的数量)。
由于此时的\(DP\)的加边方案是有序的,所以在转移完后要对方案数除以\(i\)

#include<cstdio>
#include<iostream>
#define R register int
using namespace std;
namespace Luitaryi {
template<class I> inline I g(I& x) { x=0;
    register I f=1; register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
    do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*=f;
} const int N=1010,M=10007;
int n,m,k,tot,Inv[M],c[N],ans;
int f[N][N];
inline int C(int n) {return 1ll*n*(n-1)*Inv[2]%M;}
inline void main() {
    g(n),g(m),g(k); Inv[1]=1; for(R i=2;i<=min(k,M-1);++i) Inv[i]=M-M/i*Inv[M%i]%M;
    for(R i=1,u,v;i<=m;++i) g(u),g(v),++c[u],++c[v]; for(R i=1;i<=n;++i) tot+=(c[i]&1); 
    f[0][tot]=1; for(R i=1;i<=k;++i) for(R j=0;j<=n;++j) {
        if(j>=2) f[i][j]+=f[i-1][j-2]*C(n-j+2)%M;
        f[i][j]+=f[i-1][j]*j%M*(n-j)%M;
        f[i][j]+=f[i-1][j+2]*C(j+2)%M;
        if(i>=2) f[i][j]+=M-f[i-2][j]*(C(n)-(i-2))%M;
        f[i][j]=1ll*f[i][j]*Inv[i%M]%M;
    } printf("%d\n",f[k][0]);
}
} signed main() {Luitaryi::main(); return 0;}

2019.08.16
84

转载于:https://www.cnblogs.com/Jackpei/p/11366110.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值