求用L形方块占满有障碍棋盘的方案数。
对于每个插头,定义状态有:
- 0 - 无插头
- 1 - 入插头:指向L的拐点的称为入。
- 2 - 出插头:相对地。
那么,就可以分情况讨论了。下面以左、上以及下、右的顺序描述插头。
- 均无插头
- 无和入或入和出(即L的一个端点)
- 均为出(即L的拐点)
- 均有插头
- 均为出(即L的拐点,合并两插头)
- 其余情况均不合法。
- 只有一个有插头
- 左为出:延续(下无右出)或停止(下右无)
- 左为入:延续(下无右入)或拐弯(下出右无)
- 上的情况类似。
注意到每个状态都可能扩展出很多状态,实际状态数会爆炸。
发现如果上层跑完一次的轮廓线,最右边不为无插头的状态显然是不合法的,剔除,然后就神奇地A了。。。
不过讲道理拿到以前的题目一点变化都没有。。。。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#define rep(i,j,k) for(i=j;i<k;i++)
using namespace std;
const int mod = 20110520;
const int state[] = {0, -1, 1, 0};
int get_bit(int sta, int i) { return (sta >> (i << 1)) & 3; }
int set_bit(int &sta, int i, int x) {
sta = (sta & ~(3 << (i << 1))) | (x << (i << 1));
}
const int HASH_STORAGE = 2000000, HASH_SIZE = 100007;
struct HashMap {
int key[HASH_STORAGE], val[HASH_STORAGE];
int head[HASH_SIZE], next[HASH_STORAGE], sz;
void clear() { sz = 0; memset(head, -1, sizeof head); }
void add(int k, int v) {
int t = k % HASH_SIZE;
for (int i = head[t]; i != -1; i = next[i])
if (key[i] == k) {
(val[i] += v) %= mod;
return;
}
key[sz] = k; val[sz] = v; next[sz] = head[t]; head[t] = sz++;
}
} h[2], *cur, *last;
char mp[101][101], MP[101][101];
int bx, by, n, m;
void update(int x, int y, int sta, int v) {
int l = get_bit(sta, y), s;
int t = get_bit(sta, y + 1);
#define create(i,j) s=sta,set_bit(s,y,i),set_bit(s,y+1,j),cur->add(s,v)
if (mp[x][y] == '*') {
if (!l && !t) create(0, 0);
return;
}
if (!l && !t) {
create(0, 1); create(1, 0); create(2, 2);
} else if (!l || !t) {
if (l == 2) create(0, 0), create(0, 2);
else if (l == 1) create(0, 1), create(2, 0);
if (t == 2) create(0, 0), create(2, 0);
else if (t == 1) create(1, 0), create(0, 2);
} else if (l == 1 && t == 1) create(0, 0);
}
int solve() {
int i, j, k;
cur = h; last = h + 1;
last->clear(); last->add(0,1);
rep(i,0,n) {
int rate = 2;
rep(j,0,m) {
cur->clear();
rep(k,0,last->sz) {
if (rate == 2 && get_bit(last->key[k], m)) continue;
update(i, j, last->key[k] << rate, last->val[k]);
}
rate = 0;
swap(cur, last);
}
}
int ans = 0;
rep(k,0,last->sz) if (last->key[k] == 0) {
ans = last->val[k]; break;
}
return ans;
}
int main() {
int i, j;
scanf("%d%d", &n, &m);
rep(i,0,n) scanf("%s", mp[i]);
if (m > n) {
rep(j,0,m) rep(i,0,n) MP[j][i] = mp[i][j];
swap(n, m);
rep(i,0,n) rep(j,0,m) mp[i][j] = MP[i][j];
}
bx = by = -1;
for(i=n-1;i>=0;--i) for(j=m-1;j>=0;--j)
if (mp[i][j] == '_') bx = i, by = j, i = -1, j = -1;
printf("%d", solve());
return 0;
}
2331: [SCOI2011]地板
Time Limit: 5 Sec Memory Limit: 128 MB
Submit: 827 Solved: 350
[Submit][Status][Discuss]
Description
lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西。小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?
需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。
Input
输入的第一行包含两个整数,R和C,表示客厅的大小。
接着是R行,每行C个字符。’_’表示对应的位置是空的,必须铺地板;’*’表示对应的位置有柱子,不能铺地板。
Output
输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。
Sample Input
2 2
*_
__
Sample Output
1
HINT
R∗C<=100
Source
Day1