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;
}
\(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;
}
HDU-5155 Harry And Magic Box的更多相关文章
- HDU 5155 Harry And Magic Box --DP
题意:nxm的棋盘,要求每行每列至少放一个棋子的方法数. 解法:首先可以明确是DP,这种行和列的DP很多时候都要一行一行的推过去,即至少枚举此行和前一行. dp[i][j]表示前 i 行有 j 列都有 ...
- [HDOJ 5155] Harry And Magic Box
题目链接:HDOJ - 5155 题目大意 有一个 n * m 的棋盘,已知每行每列都至少有一个棋子,求可能有多少种不同的棋子分布情况.答案对一个大素数取模. 题目分析 算法1: 使用容斥原理与递推. ...
- 【HDOJ】5155 Harry And Magic Box
DP.dp[i][j]可以表示i行j列满足要求的组合个数,考虑dp[i-1][k]满足条件,那么第i行的那k列可以为任意排列(2^k),其余的j-k列必须全为1,因此dp[i][j] += dp[i- ...
- 【hihocoder】 Magic Box
题目1 : Magic Box 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 The circus clown Sunny has a magic box. When ...
- hihocoder 1135 : Magic Box
#1135 : Magic Box 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 The circus clown Sunny has a magic box. Whe ...
- hdu 5155(递推)
Harry And Magic Box Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...
- BestCoder Round #25 1002 Harry And Magic Box [dp]
传送门 Harry And Magic Box Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/ ...
- 微软2016校园招聘在线笔试之Magic Box
题目1 : Magic Box 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 The circus clown Sunny has a magic box. When ...
- Harry And Magic Box HDU - 5155
题目链接:https://vjudge.net/problem/HDU-5155#author=0 题意:在一个n*m的方格中要满足每一行每一列至少有一个珠宝,问总共有多少种方案. 思路:利用递推的思 ...
- D. Magic Box(几何)
One day Vasya was going home when he saw a box lying on the road. The box can be represented as a re ...
随机推荐
- HDU 6444 Neko's loop ( 2018 CCPC 网络赛 && 裴蜀定理 && 线段树 )
题目链接 题意 : 给出一个 n 个元素的环.可以任意选择起点.选完起点后.可以行走 m 步.每次前进 k 个单位.所走到的点将产生正或负贡献.问你一开始得准备多少才能使得初始资金加上在环上获取最大利 ...
- Codeforces 622F The Sum of the k-th Powers ( 自然数幂和、拉格朗日插值法 )
题目链接 题意 : 就是让你求个自然数幂和.最高次可达 1e6 .求和上限是 1e9 分析 : 题目给出了最高次 k = 1.2.3 时候的自然数幂和求和公式 可以发现求和公式的最高次都是 k+1 ...
- BatchNormalization、LayerNormalization、InstanceNorm、GroupNorm、SwitchableNorm总结
https://blog.csdn.net/liuxiao214/article/details/81037416 http://www.dataguru.cn/article-13032-1.htm ...
- R_针对churn数据用id3、cart、C4.5和C5.0创建决策树模型进行判断哪种模型更合适
data(churn)导入自带的训练集churnTrain和测试集churnTest 用id3.cart.C4.5和C5.0创建决策树模型,并用交叉矩阵评估模型,针对churn数据,哪种模型更合适 决 ...
- js+jq 淡入淡出轮播(点击+定时+鼠标进入移出事件)
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title> ...
- 剑指offer32----之字形打印一颗二叉树
题目 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 思路 在这里我们如果单纯的使用队列去弄的话,会很 ...
- 第九周学习总结&实验报告(7)
完成火车站售票程序的模拟. 要求: (1)总票数1000张; (2)10个窗口同时开始卖票; (3) 卖票过程延时1秒钟; (4)不能出现一票多卖或卖出负数号票的情 况 实验代码: package y ...
- 验证TXT解析的正确性
需要用到TXT解析,不同于CNAME和A解析,可以用PING命令验证,要验证TXT解析需要用到如下命令 nslookup -q=TXT txtdemo.lei.cool 8.8.8.8 输出内容如下 ...
- leetcode-easy-others-190. Reverse Bits-NO
mycode 不会... 参考: 1. 思路: 将十进制的n转换成二进制(str) -> 利用切片.反向获取不包含0b的反转后的二进制字符串 -> 补上0(共32位) 2. class S ...
- 二、Java对返回参数进行处理(JSONObject,getJSONArray等)
一.根据返回参数格式获取其中的值 1.得到ResponseEntity<String> responseEntity对象 import org.springframework.http.R ...