zoukankan      html  css  js  c++  java
  • 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
    ",dp[n][m]);
    	}
    }
    
    signed main() {
    	_main();
    	return 0;
    }
    

    (update.in.2019.9.10)

    发现教练的一种极强的做法,可以支持(n,m)高达(1e5)的做法,时间复杂度(O(m*log_n))

    (f(i))表示至少有(i)列没有被覆盖的情况,一定不放的列有(C(m,i))中选法。

    再依次考虑每一行,剩下(m-i)列,有(2^{m-i})中放法,需要减掉全部都不放的情况,因为要保证每行至少要有一个。故有(2^{m-i}-1)种放法。

    也就是说,确定了哪些列不放之后,每一行(2^{m-i}-1)种放法,所以总的放法有((2^{m-i}-1)^n)

    (f(0))表示至少有0列一定不放,这就是所有的情况。

    其中包含了至少有1列不放的情况,需要减掉。还需要把至少有2列不放的情况加回来,依次类推,有如下结果:(ans=sum (-1)^i*C(m,i)*(2^{m-i}-1)^n)

    代码如下:

    #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=1e5+5,M=1e5+5,mod=1e9+7;
    
    bool MOP1;
    
    inline int Pow(int x,int y) {
    	int res=1;
    	while(y) {
    		if(y&1)res=(res*x)%mod;
    		x=x*x%mod,y>>=1;
    	}
    	return res;
    }
    
    int Fac[N],Inv[N],Pow_2[N],V[N];
    
    int C(int a,int b) {
    	if(a<b||b<0)return 0;
    	return 1ll*Fac[a]*((1ll*Inv[a-b]*Inv[b])%mod)%mod;
    }
    
    bool MOP2;
    
    inline void _main(void) {
    	Fac[0]=Inv[0]=Fac[1]=V[1]=Inv[1]=Pow_2[0]=1ll;
    	Pow_2[1]=2ll;
    	ret(i,2,N) {
    		Fac[i]=(1ll*Fac[i-1]*i)%mod;
    		V[i]=1ll*(mod-mod/i)*V[mod%i]%mod;
    		Inv[i]=(1ll*Inv[i-1]*V[i])%mod;
    		Pow_2[i]=1ll*Pow_2[i-1]*2%mod;
    	}
    	int n,m;
    	while(~scanf("%d %d
    ",&n,&m)) {
    		int Ans=0;
    		rep(i,0,m) {
    			int temp=1ll*C(m,i)*Pow(Pow_2[m-i]-1,n)%mod;
    			if(i&1)Ans-=temp;
    			else Ans+=temp;
    			if(Ans>mod)Ans-=mod;
    			if(Ans<0)Ans+=mod;
    		}
    		printf("%d
    ",Ans);
    	}
    
    }
    
    signed main() {
    	_main();
    	return 0;
    }
    
  • 相关阅读:
    [转载]openerp 6.0.2库存业务
    [转载]OPENERP 6.0.2中的财务概念
    负载均衡
    SQL Server高可用性部署实施 SQL server 群集
    在苹果lion中安装xcode3系列版本
    MacBook 以及 XCode 使用小结
    C++必知的几个知识点
    最新 xcode 4.2 ios 5.0无证书发布ipa文件
    负载参考
    SQLSERVER 2005 表分区说明
  • 原文地址:https://www.cnblogs.com/dsjkafdsaf/p/11459136.html
Copyright © 2011-2022 走看看