UVa 11806 Cheerleaders 带位置的不定方程,DP

题目大意:

 

在 T 个样例中,有 M*N 的矩阵,准确地染色 K 个块,使得矩阵首行、末行、首列、末列都有块被染色,输出其可能数

分类讨论:

按四角的染色情况为分类依据,以4*5的矩阵为例

1.

四角无一染色

 如图(后面的图解都按同一规则绘制,叉表示不放置,黑色表示放置,橙色表示该区域必放置一个,青色表示不必放置但可以放置)。

若 区域上放置x1个,区域下放置x2个,区域左放置x3个,区域右放置x4个,区域中放置x5个,则有:
1<=x1<=3,1<=x1<=3,1<=x1<=2,1<=x1<=2,0<=x1<=6
且x1+x2+x3+x4+x5=K,是带位置的不定方程,这样有一种情况。

2.

一角染色

如放置情况如上,则有:
0<=x1<=3,1<=x1<=3,0<=x1<=2,1<=x1<=2,0<=x1<=6
且x1+x2+x3+x4+x5=K-1,也是带位置的不定方程,这样有四种情况。

3.

侧两角染色

 如放置情况如上,则有:
0<=x1<=3,0<=x1<=3,0<=x1<=2,1<=x1<=2,0<=x1<=6
且x1+x2+x3+x4+x5=K-2,也是带位置的不定方程,这样有两种情况。

 4.

顶两角染色

  如放置情况如上,则有:
0<=x1<=3,1<=x1<=3,0<=x1<=2,0<=x1<=2,0<=x1<=6
且x1+x2+x3+x4+x5=K-2,也是带位置的不定方程,这样有两种情况。

 5.

斜对角染色

 如放置情况如上,则有:
0<=x1<=3,0<=x1<=3,0<=x1<=2,0<=x1<=2,0<=x1<=6
且x1+x2+x3+x4+x5=K-2,也是带位置的不定方程,这样有两种情况。

 6.

三角染色

  如放置情况如上,则有:
0<=x1<=3,0<=x1<=3,0<=x1<=2,0<=x1<=2,0<=x1<=6
且x1+x2+x3+x4+x5=K-3,也是带位置的不定方程,这样有四种情况。

7.

四角染色
​​​​​

   如放置情况如上,则有:
0<=x1<=3,0<=x1<=3,0<=x1<=2,0<=x1<=2,0<=x1<=6
且x1+x2+x3+x4+x5=K-4,也是带位置的不定方程,这样有一种情况。

所以这道题目实际上可以由7中规律性相同的函数计算求和得到。

对于带位置的不定方程,有这么几个参数:

不定元x1,x2,x3,x4,x5,不定元的左界l1,l2,l3,l4,l5,不定元的右界r1,r2,r3,r4,r5,不定元可使用的空间数b1,b2,b3,b4,b5,不定方程的本体x1+x2+x3+x4+x5=K,由K标记。

可以使用的状态转移:

A[i][j]表示使用前i个不定元达到的K值j时不定方程的解数

初始状态:A[0][0]=1,A[0][i]=0,K>=i>0

转移方程:A[i][j]=\sum_{t=0}^{j}A[i][j-t]*C[B[i]][t](L[i]<=t<=R[i])

下附AC 代码

#pragma warning(disable:4996)
#include<cstdio>
int T, M, N, K;
int R[6], B[6], L[6];
long long sum;
#define MOD 1000007
#define MAXN 510
long long Yong[MAXN][MAXN];
void YongInitial() {
	Yong[0][0] = 0;
	Yong[0][1] = 1;
	Yong[0][2] = 1;
	Yong[0][3] = 0;
	for (int i = 1; i < MAXN; i++) {
		Yong[i][0] = 0;
		Yong[i][i + 3] = 0;
		for (int j = 1; j <= i + 2; j++) {
			Yong[i][j] = Yong[i - 1][j - 1] + Yong[i - 1][j];
			if (Yong[i][j] >= MOD) {
				Yong[i][j] -= MOD;
			}
		}
	}
}
inline long long C(int m, int k) {
	if (m < k) {
		return 0;
	}
	if (m == 0) {
		return 1;
	}
	return Yong[m - 1][k + 1];

}
long long DP[6][MAXN];
long long ADP(int K) {
	if (K < 0) {
		return (long long)0;
		//special output
	}
	for (int i = 0; i <= K; i++) {
		DP[0][i] = 0;
	}
	DP[0][0] = 1;
	for (int i = 1; i < 6; i++) {
		for (int j = 0; j <= K; j++) {
			DP[i][j] = 0;
			for (int t = 0; t <= j; t++) {
				if (t < L[i - 1]) {
					continue;
				}
				if (t > R[i - 1]) {
					break;
				}
				DP[i][j] += DP[i - 1][j - t] * C(B[i-1], t);
				DP[i][j] %= MOD;
			}
		}
	}
	return DP[5][K];

}
long long ADD(int K) {
	long long sum;
	sum = 0;
	R[0] = B[0] = M - 2;
	R[1] = B[1] = M - 2;
	R[2] = B[2] = N - 2;
	R[3] = B[3] = N - 2;
	L[0] = L[1] = L[2] = L[3] = 1;
	//mid
	R[4] = B[4] = (M - 2) * (N - 2);
	L[4] = 0;

	sum += ADP(K);
	L[0] = L[2] = 0;
	sum += ADP(K - 1) * 4;
	L[3] = 0;
	sum += ADP(K - 2) * 2;
	L[3] = 1;
	L[1] = 0;
	sum += ADP(K - 2) * 2;
	L[3] = 0;
	sum += ADP(K - 2) * 2;
	sum += ADP(K - 3) * 4;
	sum += ADP(K - 4);

	sum %= MOD;
	return sum;
}
int main() {
	YongInitial();
	scanf("%d", &T);
	for (int i = 1; i <= T; i++) {
		sum = 0;
		scanf("%d%d%d", &M, &N, &K);
		sum = ADD(K);
		printf("Case %d: %lld\n", i, sum);

	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值