FOJ 2040 Tiling

来源:互联网 发布:mac如何投屏到电视 编辑:程序博客网 时间:2024/05/01 12:36

题意:

给定一个n*m的矩形,求用小矩形把他填满的方案。

思路:

因为n<=6,m<=10w,很明显,矩阵快速幂的既视感,关键在于矩阵如何构造。

令dp[s][i]为i列,最后一列状态为s的方案数,第二维用矩阵快速幂优化。其中方案s的定义为如果矩阵最后一列的第k个方格跟第k+1个方格是同一个矩阵的话,s的第k位(二进制)为1,则0<=s<2^(n-1)。

然后转移的时候就是对于两个状态,枚举连接的方案u,也就是一行与上一行的第k个方格如果在同一个矩阵,u的第k位就是1,所以0<=u<2^n。如果转移合法的话矩阵就要加上这个值,最后就是矩阵快速幂。

#include <iostream>#include <cstring>#include <string>#include <cstdio>#include <algorithm>#include <cmath>#include <cstdlib>#include <stack>#include <vector>using namespace std;#define LL long long#define N 1020#define M 20020#define eps 1e-10#define pli pair<LL, int>#define MP make_pair#define Pi acos(-1.0)#pragma comment(linker, "/STACK:1024000000,1024000000")#define mod 1000000007LL a[1<<6][1<<6];LL b[1<<6][1<<6];LL c[1<<6][1<<6];LL ans[1<<6][1<<6];int len;int n, m;int nn;bool check(int s, int t, int u) {for(int i = 0; i < n; ++i) {if(u >> i & 1) continue;int j = i;while(j < n && (u >> j & 1) == 0) ++j;--j;for(int k = i; k < j; ++k) {if((s >> k & 1) != (t >> k & 1))return 0;}if(i > 0 && (s >> (i - 1) & 1) == 0) return 0;if(j < n - 1 && (s >> j & 1) == 0) return 0;i = j;}return 1;}void debug(LL x[][1<<6]) {for(int i = 0; i < len; ++i) {for(int j = 0; j < len; ++j) {printf("%lld ", x[i][j]);}puts("");}puts("..");}void init() {nn = n - 1;len = 1 << nn;memset(a, 0, sizeof a);for(int s = 0; s < (1 << nn); ++s) {for(int t = 0; t < (1 << nn); ++t) {for(int i = 0; i < (1 << n); ++i) {if(check(s, t, i) && check(t, s, i))++a[s][t];}}}}void mul(LL x[][1<<6], LL y[][1<<6]) {for(int i = 0; i < len; ++i) {for(int j = 0; j < len; ++j)b[i][j] = x[i][j], c[i][j] = y[i][j];}for(int i = 0; i < len; ++i)for(int j = 0; j < len; ++j)x[i][j] = 0;for(int i = 0; i < len; ++i) {for(int j = 0; j < len; ++j) {for(int k = 0; k < len; ++k) {x[i][j] = (x[i][j] + b[i][k] * c[k][j]) % mod;}}}}void qpow(int k) {for(int i = 0; i < len; ++i)for(int j = 0; j < len; ++j)ans[i][j] = 0;for(int i = 0; i < len; ++i) ans[i][i] = 1;while(k) {if(k & 1) mul(ans, a);k >>= 1;mul(a, a);}}int main() {int cas, kk = 0;scanf("%d", &cas);while(cas--) {scanf("%d%d", &n, &m);init();qpow(m - 1);LL res = 0;for(int i = 0; i < len; ++i) {for(int j = 0; j < len; ++j)res = (res + ans[i][j]) % mod;}printf("Case %d: %I64d\n", ++kk, res);}return 0;}


0 0