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

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/88915790

题目链接

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

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

这个题上来是个三重容斥,就是你对于行、列、颜色都进行容斥。我们比较好计算的是,至多有 i i 行每行都至少有一个格子被染色,至多有 j j 列每列至多一个格子被染色,至多用了 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}
这样就可以 O ( n 3 ) O(n^3) 算出答案,是可以通过这个题的,但是我们还有更优秀的做法。

我们化一下式子: 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 = 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 = 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
前面的组合数预处理出来,后面部分每次用快速幂计算,复杂度是 O ( n c l o g m ) O(n*c*logm) 的。

代码:

#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;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/88915790