【NOIP 2018 提高组】填数游戏

传送门


problem

D 特别喜欢玩游戏。这一天,他在玩一款填数游戏。

这个填数游戏的棋盘是一个 n × m n \times m n×m 的矩形表格。玩家需要在表格的每个格子中填入一个数字(数字 0 0 0 或者数字 1 1 1),填数时需要满足一些限制。

下面我们来具体描述这些限制。

为了方便描述,我们先给出一些定义:

我们用每个格子的行列坐标来表示一个格子,即(行坐标,列坐标)。(注意: 行列坐标均从 0 0 0 开始编号)

合法路径 P P P:一条路径是合法的当且仅当:

  1. 这条路径从矩形表格的左上角的格子 ( 0 , 0 ) (0,0) (0,0) 出发,到矩形的右下角格子 ( n − 1 , m − 1 ) (n - 1,m - 1) (n1,m1) 结束;

  2. 在这条路径中,每次只能从当前的格子移动到右边与它相邻的格子,或者从当前格子移动到下面与它相邻的格子。

例如:在下面这个矩形中,只有两条路径是合法的,它们分别是 P 1 P_1 P1 ( 0 , 0 ) (0,0) (0,0) → → ( 0 , 1 ) (0,1) (0,1) → → ( 1 , 1 ) (1,1) (1,1) P 2 P_2 P2 ( 0 , 0 ) (0,0) (0,0) → → ( 1 , 0 ) (1,0) (1,0) → → ( 1 , 1 ) (1,1) (1,1)

在这里插入图片描述

对于一条合法的路径 P P P,我们可以用一个字符串 w ( P ) w(P) w(P) 来表示,该字符串的长度为 n + m − 2 n + m - 2 n+m2,其中只包含字符 “R” 或者字符 “D”, 第 i i i 个字符记录了路径 P P P 中第 i i i 步的移动方法,“R” 表示移动到当前格子右边与它相邻的格子,“D” 表示移动到当前格子下面与它相邻的格子。例如,上图中对于路径 P 1 P_1 P1,有 w ( P 1 ) w(P_1) w(P1) = = =RD”;而对于另一条路径 P 2 P_2 P2, 有 w ( P 2 ) = w(P_2) = w(P2)=DR”。

同时,将每条合法路径 P P P 经过的每个格子上填入的数字依次连接后,会得到一个长度为 n + m − 1 n + m - 1 n+m1 01 01 01 字符串,记为 s ( P ) s(P) s(P)。例如,如果我们在格子 ( 0 , 0 ) (0,0) (0,0) ( 1 , 0 ) (1,0) (1,0) 上填入数字 0 0 0,在格子 ( 0 , 1 ) (0,1) (0,1) ( 1 , 1 ) (1,1) (1,1) 上填入数字 1 1 1(见上图红色数字)。那么对于路径 P 1 P_1 P1,我们可以得到 s ( P 1 ) = s(P_1) = s(P1)= 011 011 011”,对于路径 P 2 P_2 P2,有 s ( P 2 ) = s(P_2) = s(P2)= 001 001 001”。

游戏要求小 D 找到一种填数字 0 0 0 1 1 1 的方法,使得对于两条路径 P 1 P_1 P1 P 2 P_2 P2,如果 w ( P 1 ) > w ( P 2 ) w(P_1) > w(P_2) w(P1)>w(P2),那么必须 s ( P 1 ) ≤ s ( P 2 ) s(P_1) ≤ s(P_2) s(P1)s(P2)。我们说字符串 a a a 比字符串 b b b 小,当且仅当字符串 a a a 的字典序小于字符串 b b b 的字典序。但是仅仅是找一种方法无法满足小 D 的好奇心,小 D 更想知道这个游戏有多少种玩法,也就是说,有多少种填数字的方法满足游戏的要求?

D 能力有限,希望你帮助他解决这个问题,即有多少种填 0 0 0 1 1 1 的方法能满足题目要求。由于答案可能很大,你需要输出答案对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。

数据范围: n ≤ 8 n\le 8 n8 m ≤ 1 0 6 m\le 10^6 m106


solution

这算是一道打表找规律的题,考场上写了个 d f s dfs dfs d f s dfs dfs 找了 n ≤ 3 n\le 3 n3 的规律。

具体的推导过程我就不写了,可以看看这里


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define P 1000000007
using namespace std;
int n,m,inv128=570312504,inv384=190104168;
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	if(n>m)  swap(n,m);
	if(n==1)  printf("%d\n",power(2,m));
	else  if(n==2)  printf("%d\n",mul(4,power(3,m-1)));
	else  if(n==3)  printf("%d\n",mul(112,power(3,m-3)));
	else{
		if(m==n)  printf("%d\n",(83ll*power(8,n)%P+5ll*power(2,n+7)%P)*inv384%P);
		else  printf("%d\n",(83ll*power(8,n)%P+power(2,n+8))*power(3,m-n-1)%P*inv128%P);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值