bzoj4487 [Jsoi2015]染色问题 容斥原理+组合数学

博客探讨了n*m网格图用c种颜色的涂色方法计数问题,需满足每列、每行至少有一格涂色,每种颜色至少用一次,格子可染色或不染色。给出了O(n3)的解法,还展示了化简后的n2logn做法,并给出了相关公式推导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description


有n*m的网格图和c种颜色,问有多少种涂色方法满足:

  1. 每一列至少有一个格子被涂色
  2. 每一行至少有一个格子被涂色
  3. 每一种颜色至少被用了一次
  4. 每一个格子要么被染色(1种),要么不被染色
    n,m,c≤400n,m,c\le 400n,m,c400

Solution


很显然我们有∑i=0n∑j=0m∑k=0c(−1)n−i+m−j+c−k(ni)(mj)(ck)(k+1)ij\sum_{i=0}^n\sum_{j=0}^m\sum_{k=0}^c{{\left(-1\right)}^{n-i+m-j+c-k}\binom{n}{i}\binom{m}{j}\binom{c}{k}{\left( k+1\right)}^{ij}}i=0nj=0mk=0c(1)ni+mj+ck(in)(jm)(kc)(k+1)ij
然后nmc才400^3,我们交换枚举顺序然后预处理一下次幂就可以O(n3)O(n^3)O(n3)过了,注意提一下公因式保证膜的次数比较少

贴一下化简的n2logn做法吧
二项式定理有(x−1)k=∑i=0kxi(−1)k−j(ki)\left(x-1\right)^k=\sum_{i=0}^k{x^i{(-1)}^{k-j}\binom{k}{i}}(x1)k=i=0kxi(1)kj(ik)

我们可以把柿子化成∑i=0n∑k=0c(−1)n+c−i−k(ni)(ck)∑j=0m(mj)((k+1)i)j(−1)m−j\sum_{i=0}^n\sum_{k=0}^c(-1)^{n+c-i-k}\binom{n}{i}\binom{c}{k}\sum_{j=0}^m\binom{m}{j}{\left({\left(k+1\right)}^i\right)}^j{(-1)}^{m-j}i=0nk=0c(1)n+cik(in)(kc)j=0m(jm)((k+1)i)j(1)mj
然后发现最右边和上柿是一样的形式,套进去就是
∑i=0n∑k=0c(−1)n+c−i−k(ni)(ck)((k+1)i−1)m\sum_{i=0}^n\sum_{k=0}^c(-1)^{n+c-i-k}\binom{n}{i}\binom{c}{k}{\left({\left(k+1\right)}^i-1\right)}^mi=0nk=0c(1)n+cik(in)(kc)((k+1)i1)m

然后直接做就阔以了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)

typedef long long LL;
const int MOD=1000000007;
const int N=405;

LL inv[N],fac[N],pow[N*N];

void upd(LL &x,LL v) {
	x+=v; (x>=MOD)?(x-=MOD):0;
}

int main(void) {
	freopen("data.in","r",stdin);
	fac[0]=fac[1]=1;
	rep(i,2,N-1) fac[i]=fac[i-1]*i%MOD;
	inv[0]=inv[1]=1;
	rep(i,2,N-1) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
	rep(i,2,N-1) inv[i]=inv[i-1]*inv[i]%MOD;
	int n,m,c; scanf("%d%d%d",&n,&m,&c);
	LL ans=0;
	rep(k,0,c) {
		pow[0]=1;
		rep(i,1,n*m) pow[i]=pow[i-1]*(k+1)%MOD;
		LL wjp=0;
		rep(i,0,n) {
			LL xjh=0;
			rep(j,0,m) {
				LL tmp=inv[j]*inv[m-j]%MOD*pow[i*j]%MOD;
				if ((n-i+m-j+c-k)&1) upd(xjh,MOD-tmp);
				else upd(xjh,tmp);
			}
			upd(wjp,inv[n-i]*xjh%MOD*inv[i]%MOD);
		}
		upd(ans,wjp*fac[n]%MOD*fac[c]%MOD*inv[k]%MOD*inv[c-k]%MOD*fac[m]%MOD);
	}
	printf("%lld\n", ans);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值