[氧化镍]染色

氧化镍即 N i O NiO NiO,为 N O i NOi NOi 的同分异构体。

题目

题目描述
有一个 n n n m m m 列的棋盘,最初都是白格(显然这不是国际象棋)。你可以给棋盘染色,将一整行全部染成黑色,或者将一整个对角线(从左下到右上)染成黑色。

请问,有多少种可能的最终局面?输出对 p p p 取模(不保证是质数)。

数据范围与提示
n , m ≤ 500 n,m\le 500 n,m500 1 0 8 ≤ p ≤ 1 0 9 + 7 10^8\le p\le 10^9+7 108p109+7

思路

直接考虑最终局面并不容易。我们应该考虑染色方案。

我们可以优先涂行(或者优先涂对角线)。那么选择了一些行被染色之后,就是对于对角线的决策了。

我的思考是,有三个方面:因为是优先涂行,所以对角线不能填满一行,即不能连续涂 m m m 个对角线;有的行已经涂了,所以在它这里没有 “不能连续涂 m m m 个” 的限制;有的对角线已经被涂满,即连续涂了 m m m 行,不能再决策。

好像还挺麻烦的。我甚至考虑了一下网络流,然后得到了零分的高分。正解却基本是一个思路,只是换了一个方面:找不到已经涂满的对角线,可以找没涂满的对角线。那么我们就要找没涂的行。每个没涂的行对应的对角线是 [ l i , r i ] [l_i,r_i] [li,ri],一个区间。那么所有区间的并集就是可以决策的对角线。

很巧妙的是,这同时也告诉了我们什么行是不能被涂满的,以及它什么时候被涂满。问题已经转化为了:选择若干个区间,在这些区间的并集中选择一些元素,并且每个区间的元素都不能被全选,求方案数。

把区间按照右端点排序(虽然事实上它们天然就是按照右端点有序的)。设计一个 d p \tt dp dp,令 f ( i , j ) f(i,j) f(i,j) 表示考虑了前 i i i 个区间(考虑了 [ 1 , R i ] [1,R_i] [1,Ri] 的元素),最后一个没选的元素是 R i − j R_i-j Rij,其方案数。转移时,枚举选择的上一个区间 k k k,当 R i − j ≤ R k R_i-j\le R_k RijRk 时, k k k 对应的最后一个没选的元素就直接得到了;否则 k k k 对应的最后一个没选的元素可以任选。转移系数是 2 ? 2^? 2?,即元素的自由选择。

上面这个 d p \tt dp dp 是否很容易理解呢?其实 i , j i,j i,j 类似于两个指针,一个是区间(限制),另一个是空格(用来满足限制的)。二者不断往前推进,然后时刻满足条件,就这么简单。

复杂度 O ( n 3 ) \mathcal O(n^3) O(n3)

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 505;
int dp[MaxN][MaxN<<1];
int powtwo[MaxN<<1], s[MaxN];

int main(){
	int n = readint(), m = readint();
	int Mod = readint();
	rep(i,powtwo[0]=1,n+m)
		powtwo[i] = (powtwo[i-1]<<1)%Mod;
	rep(i,1,n) drep(j,m-1,0){
		dp[i][j] = powtwo[m-1-j]; // first range
		rep(k,1,i-1) if(i-k > j){
			int fre = powtwo[min(i-k,m)-j-1];
			dp[i][j] = (dp[i][j]+1ll*s[k]*fre)%Mod;
		}
		else dp[i][j] = (dp[i][j]+dp[k][j+k-i])%Mod;
		s[i] = (s[i]+dp[i][j])%Mod;
	}
	int ans = 1; // no range chosen
	rep(i,1,n) ans = (ans+s[i])%Mod;
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值