http://hihocoder.com/contest/hiho42/problems
题意:
求一个3xN的矩形有1x2的骨牌覆盖有多少种方法
思路:
咋一看有点像轮廓线dp,但是因为N的范围太大,若果使用O(n)的算法会超时,所以可以观察矩形,
与轮廓线类似,假设使用dp,是从左到右,从上到下,那么对于第i行,那么前面的i-1行就都已经dp完毕
比如这么一个矩形,前面绿色的是已经dp完毕的,蓝色是现在正在进行的。
有轮廓线dp可知,蓝色的数量跟黄色是密切相关的,
如果用0表示未填充,1表示已填充,那么有黄色的状态可有
种,从0–>7,
那么其状态的对应关系就为:
详解可以看轮廓线dp:https://blog.csdn.net/henu_jizhideqingwa/article/details/83932874
注意,这里对蓝色的填充不可涉及到后面的部分,比如黄色为7时,蓝色的1全是上下的,而不是左右的。
7:
6:
5:
4:
3:
2:
1:
0:
那么对应这个图我们可以做出一个表
这个图可以看成一个矩阵,矩阵相乘就是总的方案数,但是对于第一行我们缺少一个黄色区域的状态,但是我们很容易想到这个黄色区域的状态应该为7,所以我们现在有了几个已知的变量,A(起点的黄色部分), M(得出的8X8矩阵), N
N(要求的矩阵的长)
那么结果就为
, 这时我们得到的是一个1XN的矩阵,
得到的是矩阵末尾列的状态的数值,理所当然我们应该得到的是状态为7的值
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int Mod = 12357;
const int maxn = 1e5 + 5;
const double eps = 0.00000001;
const int INF = 0x3f3f3f3f;
struct Adj{
int a[8][8];
Adj friend operator * (Adj a, Adj b) {
Adj c;
memset(c.a, 0, sizeof(c.a));
for (int i = 0; i < 8; i ++)
for (int j = 0; j < 8; j ++)
for (int k = 0; k < 8; k ++)
c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j] % Mod) % Mod;
return c;
}
};
Adj QuickPow(Adj a, int b) {
Adj ans = {1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
Adj base = a;
while(b) {
if(b & 1) ans = ans * base;
base = base * base;
b >>= 1;
}
return ans;
}
int main()
{
Adj a, b, c;
a = Adj{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,};
b = Adj{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,};
int N;
cin >> N;
c = QuickPow(b, N);
a = a * c;
cout << a.a[0][7] << endl;
return 0;
}