BZOJ4487: JSOI2015 染色问题 容斥原理 组合数学

19 篇文章 0 订阅
11 篇文章 0 订阅

题目链接

题意:
给你一个 n ∗ m n*m nm的棋盘,有 k k k种颜色,一开始棋盘上的每一个位置都是白色。要求你给棋盘染色,要求每行至少有一个格子被染色,每列至少有一个位置被染色,整个棋盘染完色后这 k k k种颜色都要出现过,求方案数。对 1 0 9 + 7 10^9+7 109+7取模。 n , m , c &lt; = 400 n,m,c&lt;=400 n,m,c<=400

题解:
反正我是没想出这个题。可能我容斥水平还是有点差吧。

这个题上来是个三重容斥,就是你对于行、列、颜色都进行容斥。我们比较好计算的是,至多有 i i i行每行都至少有一个格子被染色,至多有 j j j列每列至多一个格子被染色,至多用了 k k k种颜色的方案数。于是就是一个三重容斥,算容斥系数的时候三个系数要乘一起去。式子不难列: a n s = ∑ i = 0 n ∑ j = 0 m ∑ k = 0 c ( − 1 ) n − i + m − j + c − k C n i ∗ C m j ∗ C c k ∗ ( k + 1 ) i ∗ j ans=\sum_{i=0}^n\sum_{j=0}^m\sum_{k=0}^c(-1)^{n-i+m-j+c-k}C_{n}^i*C_m^j*C_c^k*(k+1)^{i*j} ans=i=0nj=0mk=0c(1)ni+mj+ckCniCmjCck(k+1)ij
这样就可以 O ( n 3 ) O(n^3) O(n3)算出答案,是可以通过这个题的,但是我们还有更优秀的做法。

我们化一下式子: ∑ i = 0 n ∑ j = 0 m ∑ k = 0 c ( − 1 ) n − i + m − j + c − k C n i ∗ C m j ∗ C c k ∗ ( k + 1 ) i ∗ j \sum_{i=0}^n\sum_{j=0}^m\sum_{k=0}^c(-1)^{n-i+m-j+c-k}C_{n}^i*C_m^j*C_c^k*(k+1)^{i*j} i=0nj=0mk=0c(1)ni+mj+ckCniCmjCck(k+1)ij = ∑ i = 0 n ∑ k = 0 c ( − 1 ) n − i + m + c − k C n i ∗ C c k ∗ ∑ j = 0 m C m j ∗ ( − 1 ) j ∗ ( k + 1 ) i ∗ j =\sum_{i=0}^n\sum_{k=0}^c(-1)^{n-i+m+c-k}C_{n}^i*C_c^k*\sum_{j=0}^mC_m^j*(-1)^j*(k+1)^{i*j} =i=0nk=0c(1)ni+m+ckCniCckj=0mCmj(1)j(k+1)ij 我们发现后面可以看作一个二项式展开式,于是进行化简 = ∑ i = 0 n ∑ k = 0 c ( − 1 ) n − i + m + c − k C n i ∗ C c k ∗ ( 1 − ( k + 1 ) i ) m =\sum_{i=0}^n\sum_{k=0}^c(-1)^{n-i+m+c-k}C_{n}^i*C_c^k*(1-(k+1)^i)^m =i=0nk=0c(1)ni+m+ckCniCck(1(k+1)i)m
前面的组合数预处理出来,后面部分每次用快速幂计算,复杂度是 O ( n ∗ c ∗ l o g m ) O(n*c*logm) O(nclogm)的。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,c;
const long long mod=1e9+7;
long long ans,jie[410],ni[410];
inline long long ksm(long long x,long long y)
{
	long long res=1;
	while(y)
	{
		if(y&1)
		res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
int main()
{
	scanf("%d%d%d",&n,&m,&c);
	jie[0]=1;
	for(int i=1;i<=400;++i)
	jie[i]=jie[i-1]*i%mod;
	ni[400]=ksm(jie[400],mod-2);
	for(int i=399;i>=0;--i)
	ni[i]=ni[i+1]*(i+1)%mod;
	for(int i=0;i<=n;++i)
	{
		for(int k=0;k<=c;++k)
		{
			int opt=1;
			if((n+m+c-i-k)&1)
			opt=-1;
			ans=(ans+opt*jie[n]*ni[i]%mod*ni[n-i]%mod*jie[c]%mod*ni[k]%mod*ni[c-k]%mod*ksm((1-ksm(k+1,i)+mod)%mod,m)%mod+mod)%mod;
		}
	}
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值