JZOJ 7047. 2021.04.07【2021省赛模拟】染色(计数+DP)

JZOJ 7047. 2021.04.07【2021省赛模拟】染色

题目大意

  • 一个 n ∗ m n*m nm的棋盘,一次染色可以把任何一行或任何一条左下-右上方向的对角线上所有格子都染黑,求各种染色方案可以得到的不同最终形态数。
  • n , m ≤ 500 n,m\le500 n,m500

题解

  • 最终形态比较难统计,可以改为统计不同的染色方案。由于不同的染色方案可能会得到相同的最终形态,所以需要规定一些染色方案为“合法”的以保证不重复统计。
  • 可以让每种最终形态对应上唯一一种染色方案。具体的,使最终形态中有整行的必须由整行的染,其他剩余未染的才用对角线染。
  • 把行从上到下 1 − n 1-n 1n标号,把对角线从左上到右下 1 − ( n + m − 1 ) 1-(n+m-1) 1(n+m1)标号,则可以知道怎么样的染色方案为“合法“的:
  • 1、第 i i i行不染色,则 i i i i + m − 1 i+m-1 i+m1号对角线不能都染色,不然会覆盖满一整行;
  • 2、第 i i i条对角线染色,则 m a x ( i + 1 − m , 1 ) max(i+1-m,1) max(i+1m,1) m i n ( n , i ) min(n,i) min(n,i)号横行(即这条对角线经过的横行)不能都染色,不然这条对角线染色没有用。
  • 更抽象地,看作长为 n n n n + m − 1 n+m-1 n+m1的两个序列 A , B A,B A,B,每个数可以选或不选, A i A_i Ai不选则 B i B_i Bi往后不能连续选超过 m m m个, B i B_i Bi选则 A m i n ( i , n ) A_{min(i,n)} Amin(i,n)往前不能连续选超过 m i n ( n , i ) − m a ( i + 1 − m , 1 ) + 1 min(n,i)-ma(i+1-m,1)+1 min(n,i)ma(i+1m,1)+1个。
  • 发现任一序列 i i i处的限制总是和另一序列的 i i i处相关,则两序列同一位置一起转移, B B B序列多出的部分再单独转移即可。
  • f i , j , k f_{i,j,k} fi,j,k表示转移到第 i i i位, B B B序列从当前还能往后连续选 j j j个, A A A序列到当前已经连续选了 k k k个,分 A i A_i Ai选或不选, B i B_i Bi选或不选来转移。要注意在 i > n i>n i>n时, k k k表示的是 A A A n n n已经连续选的个数,因为 B B B限制的是 A m i n ( i , n ) A_{min(i,n)} Amin(i,n)
  • 第一维可以滚动,时间复杂度 O ( n 3 ) O(n^3) O(n3)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 510
int f[2][N][N];
int main() {
	int n, m, P, i, j, k;
	scanf("%d%d%d", &n, &m, &P);
	f[0][m + 1][0] = 1;
	for(i = 0; i <= n + m - 2; i++) {
		int o = i % 2;
		memset(f[o ^ 1], 0, sizeof(f[o ^ 1]));
		for(j = 0; j <= m + 1; j++) {
			for(k = 0; k <= n; k++) if(f[o][j][k]) {
				if(i + 1 <= n) {
					(f[!o][m + 1][0] += f[o][j][k]) %= P; // a no b no
					(f[!o][m + 1][k + 1] += f[o][j][k]) %= P;// a yes b no
					if(j && m > 1) (f[!o][min(m - 2, j > m ? j : j - 1)][0] += f[o][j][k]) %= P;//a no b yes
					if(j && k < i - max(i + 1 + 1 - m, 1) + 1) (f[!o][j > m ? j : j - 1][k + 1] += f[o][j][k]) %= P; //a yes b yes
				}
				else {
					(f[!o][m + 1][k] += f[o][j][k]) %= P; // a no b no
					if(j && k < n - max(i + 1 + 1 - m, 1) + 1) (f[!o][j > m ? j : j - 1][k] += f[o][j][k]) %= P;//a no b yes
				}
			}	
		}
	}
	int ans = 0;
	for(j = 0; j <= m + 1; j++)
		for(k = 0; k <= n; k++) (ans += f[(n + m - 1) % 2][j][k]) %= P;
	printf("%d\n", ans);
	return 0;
}

自我小结

  • 这题想到统计染色方案后,先写了暴力DFS,在剪枝优化的过程中发现了限制的关联性,于是顺理成章地用DP设状态解决。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值