Problem
-
给定你一个 n ⋅ m n·m n⋅m网格棋盘,要求用 0 / 1 0/1 0/1填充每一个格子.
-
使得任意两条从原点 ( 0 , 0 ) (0,0) (0,0)到 ( n − 1 , m − 1 ) (n-1,m-1) (n−1,m−1)的不完全相交路径,先走右方向(字典序大)的路径的 01 01 01路径字典序相对小.
-
求方案数.
Data constraint
- n ≤ 8 , m ≤ 1 0 6 n\le 8, m \le 10^6 n≤8,m≤106
Solution
-
其实这题可以不用 D P DP DP.
-
显然对于棋盘上的某一种填法 A A A,满足 ∀ i ∈ ( 0 , n ) , j ∈ ( 0 , m ) , A i , j ≥ A i − 1 , j + 1 \forall i\in(0,n),j\in(0,m),A_{i,j}\ge A_{i-1,j+1} ∀i∈(0,n),j∈(0,m),Ai,j≥Ai−1,j+1
-
且显然如果当 A i , j = A i − 1 , j + 1 A_{i,j}=A_{i-1,j+1} Ai,j=Ai−1,j+1时, ( i , j + 1 ) → ( n , m ) (i,j+1)\rightarrow(n,m) (i,j+1)→(n,m)形成一个封闭局面,这个局面的每条对角线上的数必须相同,这样是为了保证 ( i , j + 1 ) → ( n , m ) (i,j+1)\rightarrow(n,m) (i,j+1)→(n,m)的每条路径相同.
-
那么现在就可以分类讨论了:
-
( 0 , 1 ) , ( 1 , 0 ) (0,1),(1,0) (0,1),(1,0)两格相同:
-
这种情况下,只需要使得 ( 1 , 1 ) → ( n , m ) (1,1)\rightarrow(n,m) (1,1)→(n,m)的所有对角线上数字相同.
-
同时第一列和第一行会受其影响,仔细推一波式子可以搞定.
-
答案形如 4 x ∗ 3 y ∗ 2 z 4^x*3^y*2^z 4x∗3y∗2z.
-
这是最简单的情况。
-
-
( 0 , 1 ) , ( 1 , 0 ) (0,1),(1,0) (0,1),(1,0)两格不同:
-
( 0 , 2 ) , ( 1 , 1 ) , ( 2 , 0 ) (0,2),(1,1),(2,0) (0,2),(1,1),(2,0)相同:
-
这一种情况也相对简单,也是一波式子可以搞定的.
-
这是次简单的情况,其实与第一种情况完全类似,答案的形式也是一样的.
-
-
( 0 , 2 ) , ( 1 , 1 ) , ( 2 , 0 ) (0,2),(1,1),(2,0) (0,2),(1,1),(2,0)其中一对相同:
-
不难发现 ( 1 , 1 ) (1,1) (1,1)与 ( 2 , 0 ) (2,0) (2,0)或 ( 0 , 2 ) (0,2) (0,2)相同是等价的.
-
不妨假设 ( 1 , 1 ) , ( 2 , 0 ) (1,1),(2,0) (1,1),(2,0)相同.
-
设 f [ i ] f[i] f[i]表示到第 i i i条对角线上时,在前两行的第 i − 1 i-1 i−1条斜线上及以前有过“配对成功”的方案数。
-
“配对成功” 是指在最上边两行中,第二行的某一格与其斜上方的格相同,称之为配对成功。
-
因为一旦配对成功,接下来第二行的填数就会有限制.
-
当第一行与第二行全部没有配对成功时,当前的一条对角线实际上有 5 5 5种填法,而当第 i i i条对角线配对成功时,实际上只有 4 4 4种填法.
-
那么考虑一下转移就可以写出: f [ i ] = f [ i − 1 ] ∗ 4 + 4 ∗ 5 f[i] = f[i - 1] * 4 + 4 * 5 f[i]=f[i−1]∗4+4∗5
-
表示的含义是:
-
(在 i − 2 i-2 i−2条斜线上以前就匹配成功的方案 f [ i − 1 ] f[i-1] f[i−1]) *(当前第 i i i条对角线的 4 4 4种填法) + ( i − 1 i-1 i−1条斜线第一次匹配成功的 4 4 4种填法) * (当前第 i i i条对角线因不受任何影响所以有 5 5 5种填法.)
-
-
-
-
这样就可以计算出当 n = m n=m n=m的答案了.
-
当 m = n + 1 m = n + 1 m=n+1时,前两种分类讨论是很容易继续推广的,关键是第三种 分类讨论应当如何推广.
-
不妨画一下图,我们发现,实质上答案是 A N S = ( 4 + 4 ∗ 4 + ( f [ n ] ∗ 3 + 4 ∗ 3 ) ∗ 2 ) ∗ 2 n − 1 ANS=(4 + 4 * 4 + (f[n] * 3 + 4 * 3) * 2) * 2^{n-1} ANS=(4+4∗4+(f[n]∗3+4∗3)∗2)∗2n−1
-
至于为什么也是按上面第三种分类讨论一样,根据最后一条对角线是否匹配成功进行讨论.
-
而当 m = n + 1 m=n+1 m=n+1时, ( 2 , 0 ) , ( 1 , 1 ) , ( 0 , 2 ) (2,0),(1,1),(0,2) (2,0),(1,1),(0,2)哪两个相同是会有影响的,根据上面的推法,也不难推出另外一种的答案实质上是 A N S = ( f [ n ] ∗ 3 + 4 ∗ 5 ) ∗ 2 n ANS = (f[n] * 3 + 4 * 5) * 2^n ANS=(f[n]∗3+4∗5)∗2n
-
并且继续仔细推式子后可以发现当 m > n + 1 m>n+1 m>n+1时, m m m每加一,答案会乘 3 3 3.
-
于是时间复杂度是 O ( n + l o g 2 m ) O(n+log_2m) O(n+log2m),可以矩阵转移做到 O ( l o g n + l o g m ) O(log_n+log_m) O(logn+logm)
-
参考代码:
F(i, 1, n - 4)
f[i + 1] = (4 * 5 + f[i] * 4) % Mo; // 4表示上一条对角线第一次配对成功
ans = 2 * (3 + 4 * 3 + f[n - 3] * 2) * ksm(2, n - 1); // n==m 时的
a1 = ksm(4, n - 2) * ksm(2, n + 1) % Mo; //第一种情况
a2 = 5 * ksm(4, n - 4) * ksm(2, n + 1) % Mo; //第二种情况
if (n == m) { printf("%d", (ans + a1 + a2) % Mo); return 0; }
ANS1 = (4 + 4 * 4 + (f[n - 3] * 3 + 4 * 3) * 2) * ksm(2, n - 1) % Mo; // n+1==m的第一种情况
ANS2 = (f[n - 3] * 3 + 4 * 5) * ksm(2, n) % Mo; //n+1==m的第二种情况
printf("%d", ((((a1 + a2) * 3 + ANS1 + ANS2) % Mo) * ksm(3, m - n - 1)) % Mo);