#组合,容斥#JZOJ 3332 棋盘游戏

比赛

题目

有一个N*M的棋盘,初始每个格子都是白色的。
行或列操作是指选定某一行或列,将这行或列所有格子的颜色取反(黑白互换)。
进行R次行操作C次列操作(可能对某行或者某列操作了多次),最后棋盘上有S个黑色格子。
问有多少种不同的操作方案。两种操作方案不同,当且仅当对某行或者某列操作次数不同(也就是说与操作的顺序无关)。求方案数 m o d    1 0 9 + 7 \mod 10^9+7 mod109+7


分析

细节太多,要慢慢讲。
首先要知道条件,设有X行,Y列有效取反,那么 S = X N + Y M − 2 X Y S=XN+YM-2XY S=XN+YM2XY才会成立。
所以其实可以只枚举 X X X,就可以求出 Y = S − X N M − 2 X Y=\frac{S-XN}{M-2X} Y=M2XSXN
首先对于特殊情况分母为0时,对原式化简得 S = X N S=XN S=XN
∴ S = M ∗ N / 2 ( ∵ M − 2 X = 0 ∴ X ∗ 2 = M ) \therefore S=M*N/2(\because M-2X=0\therefore X*2=M) S=MN/2(M2X=0X2=M)
所以当分母为0且 S = M ∗ N / 2 且 X ∗ 2 = M S=M*N/2且X*2=M S=MN/2X2=M时Y=0
否则当 S − X N M − 2 X = ⌊ S − X N M − 2 X ⌋ \frac{S-XN}{M-2X}=\lfloor \frac{S-XN}{M-2X} \rfloor M2XSXN=M2XSXN时就可以计算出Y
Y ≥ 0 , C − Y ≥ 0 , C − Y m o d    2 = 0 Y\geq0,C-Y\geq0,C-Y \mod 2=0 Y0,CY0,CYmod2=0,那怎么算呢。
a n s + = C ( n , x ) ∗ C ( m , y ) ∗ C ( ( r − x ) / 2 + n − 1 , n − 1 ) ∗ C ( ( c − y ) / 2 + m − 1 , m − 1 ) ans+=C(n,x)*C(m,y)*C((r-x)/2+n-1,n-1)*C((c-y)/2+m-1,m-1) ans+=C(n,x)C(m,y)C((rx)/2+n1,n1)C((cy)/2+m1,m1)
前面好理解,n行选择x行的组合方案*m行选择y行的组合方案,后面是处理重复的取反,为
( r − x ) / 2 次 , ( c − y ) / 2 次 (r-x)/2次,(c-y)/2次 (rx)/2(cy)/2无效取反,可以用隔板法,把无效取反放入隔板有多少种方案,给无效取反加上n-1(m-1)是为了留出空间。


代码

#include <cstdio>
#define mod 1000000007
#define bll long long
using namespace std;
bll n,m,r,c,f[150001],ans,s;
bll ksm(bll x,bll y){//快速幂
	bll ans=1;
	while (y){
		if (y&1) ans=(ans*x)%mod;
		x=(x*x)%mod; y>>=1;
	}
	return ans;
}
bll C(bll n,bll m){
	bll a=f[n],b=1ll*f[m]*f[n-m]%mod;
	return a*ksm(b,mod-2)%mod;//乘法逆元
}
int main(){
	scanf("%lld%lld%lld%lld%lld",&n,&m,&r,&c,&s); f[0]=f[1]=1ll;
	for (int i=1;i<=150000;i++) f[i]=f[i-1]*i%mod;//求阶乘
	for (int x=r%2;x<=r;x+=2){
		if ((x<<1)==n){//分母为0
			if (s<<1==n*m&&c%2==0) ans=(ans+(C(n,x)*C(m,0)%mod*C(((r-x)>>1)+n-1,n-1)*C((c>>1)+m-1,m-1)%mod)%mod)%mod;//如分析所示
		}
		else if ((s-x*m)%(n-(x<<1))==0){
			long long y=(s-x*m)/(n-(x<<1));//求出y
			if(y<0||c-y<0||(c-y)&1) continue;//不满足条件
			ans=(ans+(C(n,x)*C(m,y)%mod*C(((r-x)>>1)+n-1,n-1)%mod*C(((c-y)>>1)+m-1,m-1)%mod)%mod)%mod;//计算答案
		}
	}
	return !printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值