题目链接
题意
在一个 R ×C 的网格中有 n 个黑格,其余均为白格。要求用 1×2 的骨牌覆盖所有白格(每个白格恰好被一块骨牌覆盖,且所有黑格均没有被覆盖),计算有多少种方案,并输出方案总数除以 10000007 后的余数。其中,1≤R≤4, 1≤C≤100000000, 0≤N≤100。
分析
插头DP里面的覆盖问题,本题是UVa11270 Tiling Dominoes的升级版:1、有障碍格;2:C可达
1
0
8
10^8
108规模,需要借助快速矩阵幂加速。
需要构造状态转移矩阵,首先是障碍格的矩阵,然后非障碍格的矩阵需要区分是否为列首(因此有两种),总共三种状态转移矩阵。
由于三种矩阵都非常稀疏,进行矩阵乘法时可以只在非0元素处相乘并累加,这样做会大幅优化时间效率。
一个坑点:测试数据是给出各障碍格的坐标,坐标可能重复!。
AC 代码
#include <iostream>
#include <algorithm>
using namespace std;
#define M 10000007
#define N 102
#define T 16
int a[T][T], b[T][T], d[T][T], e[T][T], f[T][T], g[T][T], t[T][T], x[N], y[N], s[N], r, c, m, n, h, kase = 0;
bool cmp(int i, int j) {
return y[i] < y[j] || (y[i] == y[j] && x[i] < x[j]);
}
void copy(const int (&a)[T][T], int (&b)[T][T]) {
for (int i=0; i<m; ++i) for (int j=0; j<m; ++j) b[i][j] = a[i][j];
}
void mul(const int (&a)[T][T], const int (&b)[T][T], int (&c)[T][T]) {
for (int i=0; i<m; ++i) for (int j=0; j<m; ++j) c[i][j] = 0;
for (int i=0; i<m; ++i) for (int k=0; k<m; ++k) if (a[i][k]) for (int j=0; j<m; ++j) if (b[k][j])
c[i][j] = (c[i][j] + a[i][k]*(long long)b[k][j]) % M;
}
void pow(int x) {
copy(f, t);
while (x) {
if (x & 1) copy(e, g), mul(g, t, e);
if (x == 1) return;
copy(t, g); mul(g, g, t); x >>= 1;
}
}
int solve() {
for (int i=0; i<n; ++i) cin >> x[i] >> y[i], s[i] = i;
sort(s, s+n, cmp);
m = 1<<r; h = m>>1;
for (int i=0; i<m; ++i) {
for (int j=0; j<m; ++j) a[i][j] = b[i][j] = d[i][j] = 0, e[i][j] = i==j;
if (i&h) {
a[i][(i^h) << 1] = b[i][(i^h) << 1] = d[i][(i^h) << 1 | 1] = 1;
if (~i&1) b[i][(i^h) << 1 | 3] = 1;
} else a[i][i<<1 | 1] = b[i][i<<1 | 1] = 1;
}
copy(a, f);
for (int k=1; k<r; ++k) copy(f, g), mul(g, b, f);
int rr = 0, cc = 0;
for (int i=0; i<n; ++i) {
if (cc > y[s[i]]) continue;
if (cc < y[s[i]]) {
if (rr > 0) {
while (rr++ < r) copy(e, g), mul(g, b, e);
rr = 0; ++cc;
}
if (cc < y[s[i]]) pow(y[s[i]] - cc);
}
while (rr <= x[s[i]]) copy(e, g), mul(g, rr==x[s[i]] ? d : (rr ? b : a), e), ++rr;
rr == r ? (cc = y[s[i]] + 1, rr = 0) : cc = y[s[i]];
}
if (rr > 0) {
while (rr++ < r) copy(e, g), mul(g, b, e);
rr = 0; ++cc;
}
if (cc < c) pow(c - cc);
return e[m-1][m-1];
}
int main() {
while (cin >> r >> c >> n && r) cout << "Case " << ++kase << ": " << solve() << endl;
return 0;
}


2926

被折叠的 条评论
为什么被折叠?



