HDU-5155 Harry And Magic Box

题目描述

\(n*m\)的矩阵内每一行每一列都有钻石,问钻石分布的种类?

答案有可能很大,所以输出答案对\(1000000007\)取模。

Input

对于每个测试用例,有两个整数\(n\)\(m\)表示框的大小。\(0< N,M<50\)

Output

输出每组数据的分发数.

Sample Input

1 1
2 2
2 3

Sample Output

1
7
25

这是一道比较优秀的容斥题。

首先,我们很显然的看到\(n,m\)范围都不是很大,考虑\(dp\)

定义\(dp[i][j]\)表示有\(i\)行和\(j\)列已经满足条件的方案数。

至于为什么是有\(i\)行和\(j\)列,而不是前\(i\)行和\(j\)列,因为相对应前\(i\)行,有\(i\)行会较简单,比较好求。

求完后直接容斥即可。

下面有了定义我们就可以直接开始大力\(dp\)了。

对于当前考虑的\(i\)\(j\)列,若不考虑钻石的放置一共有\(2^{i*j}\)中取法。

而现在我们需要将其中不满足条件的方案给去掉。

对于有\(i\)\(j\)列的,我们需要去掉的是少于\(i\)\(j\)列的,而我们的\(dp\)是从小到大枚举的。

所以,当我们求\(dp[i][j]\)时,\(dp[i-1][j]...\)等的\(dp\)值我们都已经求出来了。

而把\(i\)\(j\)列的方案中去掉\(a\)\(b\)列的方案不就是从\(i\)\(j\)列中选\(a\)\(b\)列吗?

行和列可以分开来算,即从\(i\)\(j\)列中选\(a\)\(b\)列的方案数=从\(i\)行中选\(a\)行的方案数*从\(j\)列中选\(b\)列的方案数。

\(C(i,a)*C(n,j)\)

同样我们枚举所有小于等于\((i,j)\)的点对,同时减去这些不满足条件的方案就好了。

注意负数要加上模数再取模

代码如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;

#define int long long
#define reg register
#define Raed Read
#define clr(a,b) memset(a,b,sizeof a)
#define Mod(x) (x>=mod)&&(x-=mod)
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)>(b)?(b):(a))
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
#pragma GCC target("avx,avx2,sse4.2")
#pragma GCC optimize(3)

inline int Read(void) {
    int res=0,f=1;
    char c;
    while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    do res=(res<<3)+(res<<1)+(c^48);
    while(c=getchar(),c>=48&&c<=57);
    return f?res:-res;
}

template<class T>inline bool Min(T &a, T const&b) {
    return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a, T const&b) {
    return a<b?a=b,1:0;
}

const int N=55,M=1e5+5,mod=1e9+7;

bool MOP1;

int n,m,Fac[N],Inv[N],V[N],Pow[N*N],dp[N][N];

int C(int a,int b) {
    return ((Fac[a]*Inv[a-b])%mod*Inv[b])%mod;
}

bool MOP2;

inline void _main(void) {
    Fac[0]=Inv[0]=Fac[1]=V[1]=Inv[1]=Pow[0]=1ll;
    rep(i,2,50) {
        Fac[i]=(Fac[i-1]*i)%mod;
        V[i]=(mod-mod/i)*V[mod%i]%mod;
        Inv[i]=(Inv[i-1]*V[i])%mod;
    }
    rep(i,1,2500)Pow[i]=Pow[i-1]*2ll%mod;
    rep(i,0,50)rep(j,0,50) {
        dp[i][j]=Pow[i*j];
        rep(a,0,i)rep(b,0,j) {
            if(a==i&&b==j)continue;
            dp[i][j]=(dp[i][j]-((dp[a][b]*C(i,a))%mod*C(j,b))%mod)%mod;
        }
        dp[i][j]=(dp[i][j]+mod)%mod;
    }
    while(~scanf("%lld %lld",&n,&m)) {
        printf("%lld\n",dp[n][m]);
    }
}

signed main() {
    _main();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/dsjkafdsaf/p/11459136.html