problem
小 D 特别喜欢玩游戏。这一天,他在玩一款填数游戏。
这个填数游戏的棋盘是一个 n × m n \times m n×m 的矩形表格。玩家需要在表格的每个格子中填入一个数字(数字 0 0 0 或者数字 1 1 1),填数时需要满足一些限制。
下面我们来具体描述这些限制。
为了方便描述,我们先给出一些定义:
我们用每个格子的行列坐标来表示一个格子,即(行坐标,列坐标)。(注意: 行列坐标均从 0 0 0 开始编号)
合法路径 P P P:一条路径是合法的当且仅当:
-
这条路径从矩形表格的左上角的格子 ( 0 , 0 ) (0,0) (0,0) 出发,到矩形的右下角格子 ( n − 1 , m − 1 ) (n - 1,m - 1) (n−1,m−1) 结束;
-
在这条路径中,每次只能从当前的格子移动到右边与它相邻的格子,或者从当前格子移动到下面与它相邻的格子。
例如:在下面这个矩形中,只有两条路径是合法的,它们分别是 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+m−2,其中只包含字符 “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+m−1 的 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 n≤8, m ≤ 1 0 6 m\le 10^6 m≤106。
solution
这算是一道打表找规律的题,考场上写了个 d f s dfs dfs 套 d f s dfs dfs 找了 n ≤ 3 n\le 3 n≤3 的规律。
具体的推导过程我就不写了,可以看看这里。
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;
}