题目描述

在\(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;
}

\(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",&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\n",Ans);
} } signed main() {
_main();
return 0;
}

最新文章

  1. C# 关于委托和事件的妙文:通过一个例子详细介绍委托和事件的作用;Observer模式简介
  2. http协议(二)请求和响应报文的构成
  3. ZookeeperNet太难用,写了个RetryHelper来进行配套使用
  4. IL查看泛型
  5. iOS 中contraints居中对齐的一点心得
  6. Ext grid checkbox 分页 翻页 勾选 问题
  7. bzoj4229: 选择
  8. ios 录音
  9. Hadoop运行中遇到的错误调试
  10. python常用执行方式&amp;变量&amp;input函数
  11. [转]Android长度单位详解
  12. 在eclipse下,用Maven创建Spring MVC工程
  13. ORA-19566: exceeded limit of 0 corrupt blocks for file E:\xxxx\&lt;datafilename&gt;.ORA.
  14. 第一个spring,总结!
  15. 【Spark】Spark-reduceByKey-深入理解
  16. tomcat服务器宕机解决方案
  17. ImportError: No module named arcpy
  18. 20165324《Java程序设计》第一周
  19. hdu 4825 Xor Sum trie树
  20. Datastructure

热门文章

  1. Eclipse 导入逆向工程
  2. QT5线程关闭
  3. VS Code 最好用的 Markdown 插件
  4. Linux下运行scala语言的jar包
  5. 如何将JS中object转换为字符串
  6. 如何快速查询中科院JCR分区和汤森路透JCR分区
  7. MySQL + centos +主从复制
  8. git多人参与的项目 -> 分支代码如何合并到主干
  9. hibernate entitymanager的理解
  10. 将文件夹上传到FTP服务器,遍历上传,,,文件夹不能直接上传到FTP服务器上。。。