[GDKOI2016]地图
前话:这可能是我做的所有题中时间跨度最大的题了,5.4-5.13,因为中间停竞赛了(
Description
给定一个 \(n*m\) 的地图,每个地方可能是平地,障碍物,出口,入口,神器或未知。现在你要求有多少种地图,使得它满足:
-
入口,出口和神器均出现一次且不在同一个格子里;
-
上述三者两两四联通。
答案对 \(10 ^ 9 + 7\) 取模。
\(1\leq n,\ m \leq 7\)
Analysis
首先你有一个 \(O(5 ^ {nm})\) 的算法,然后加个特判条件 1 就可以优化到大概 \(O(2 ^{nm})\) 了。
(好吧我在说废话)
仔细想想:方格图,连通块,\(7*7\)(指比较小),方案数。
我们发现这到题无论从题目背景还是数据范围都很插头 DP 。
哟西,开干,不想做了溜了。
Solution
连通块的类似问题我们是已经见识过的了,用最小表示法维护一整行的联通情况。
由于一行最多 \(7\) 个格子,最多会有 \(4\) 个单独的连通块,加上不属于任何连通块的一种,再怎么着 \(8\) 进制状态也是够了的。
但是好像有些特殊限制,那我们可以在末尾加三个位置,专门记录入口。出口和神器处在的连通块。
然后最统计答案的话,按照题目要求,只要全部属于一个连通块就行。
那现在只剩下转移了哈哈。
不过意外的发现转移分讨量小的惊人:
-
障碍物直接把原位置变成零就能插入;
-
其他非问号该是啥是啥;
-
问号分成上面两种情况做就行。
然后 \(2,\ 3\) 情况插入非问号的时候注意连通块的变化:
-
如果是入口,出口或神器要判断是否已经插入过,有的话就当作什么事没发生,默默跳过;
-
如果左边和上边都不属于连通块,新开一个连通块;
-
如果仅左边有连通块或仅上边有连通块,谁有就继承谁的;
-
都有的话就把全部属于两个连通块的格子变成其中一个。
结束之后记得如果插入的是入口,出口或神器要把单独拎出来的位置也标记好。
Code
Code
/*
2 3
S#E
???
*/
#include
//#define int long long
using namespace std;
typedef long long ll;
const int N = 12, zs = 299987, mod = 1e9 + 7;
int n, m, a[N][N], c[N], pl[N], sig[N], co1, co2, co3, ans;
int now, las, inc[N], fst[zs + 10], tot[2];
char s[N];
struct mdzz {int nxt, bt[2], val[2];} e[(1 << 24) + 10];
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();}
while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();}
return s * w;
}
inline int mini(int x) {
memset(pl, 0, sizeof(pl));
int cnt = 0;
for (int i = 1; i <= m + 3; ++i) {
c[i] = x & 7, x >>= 3;
if (!c[i]) continue;
if (!pl[c[i]]) pl[c[i]] = ++cnt;
c[i] = pl[c[i]];
}
co1 = c[m + 1]; co2 = c[m + 2]; co3 = c[m + 3];
for (int i = m + 3; i >= 1; --i) x = (x << 3) | c[i];
return x;
}
inline void insert(int bit, int num) {
int u = bit % zs + 1;
for (int i = fst[u]; i; i = e[i].nxt) {
if (e[i].bt[now] == bit) {
e[i].val[now] += num;
if (e[i].val[now] >= mod) e[i].val[now] -= mod;
return ;
}
}
e[++tot[now]].nxt = fst[u];
e[tot[now]].bt[now] = bit;
e[tot[now]].val[now] = num;
fst[u] = tot[now];
}
inline void obstacle(int u, int v, int bit, int num) {
int x = bit;
for (int i = 1; i <= m + 3; ++i) c[i] = x & 7, x >>= 3;
c[v] = 0;
for (int i = m + 3; i >= 1; --i) x = (x << 3) | c[i];
x = mini(x);
insert(x, num);
}
inline void puspace(int u, int v, int bit, int num) {
int x = bit;
for (int i = 1; i <= m + 3; ++i) c[i] = x & 7, x >>= 3;
if (a[u][v] == 1 && c[m + 1]) return ;
if (a[u][v] == 2 && c[m + 2]) return ;
if (a[u][v] == 3 && c[m + 3]) return ;
if (!c[v - 1] && !c[v]) c[v] = 7;
else if (!c[v - 1] && c[v]) ;
else if (c[v - 1] && !c[v]) c[v] = c[v - 1];
else {
int s = c[v - 1], t = c[v];
for (int i = 1; i <= m + 3; ++i) {
if (c[i] == t) c[i] = s;
}
}
if (a[u][v] == 1) c[m + 1] = c[v];
else if (a[u][v] == 2) c[m + 2] = c[v];
else if (a[u][v] == 3) c[m + 3] = c[v];
for (int i = m + 3; i >= 1; --i) x = (x << 3) | c[i];
x = mini(x);
insert(x, num);
}
inline void Plugdp(int i, int j, int bit, int num) {
if (a[i][j] == 5) obstacle(i, j, bit, num);
else if (!a[i][j]) {
int pre = a[i][j];
for (int k = 1; k <= 5; ++k) {
if (k <= 3 && sig[k]) continue;
a[i][j] = k;
if (k == 5) obstacle(i, j, bit, num);
else puspace(i, j, bit, num);
}
a[i][j] = pre;
}
else puspace(i, j, bit, num);
}
inline void plug() {
e[tot[now] = 1].val[now] = 1; e[1].bt[now] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
memset(fst, 0, sizeof(fst));
swap(las, now); tot[now] = 0;
for (int k = 1; k <= tot[las]; ++k) {
int bit = e[k].bt[las], num = e[k].val[las];
Plugdp(i, j, bit, num);
}
}
}
}
inline void calc() {
for (int i = 1, x; i <= tot[now]; ++i) {
x = e[i].bt[now];
for (int j = 1; j <= m + 3; ++j) {
c[j] = x & 7; x >>= 3;
}
if (!c[m + 1] || !c[m + 2] || !c[m + 3]) continue;
else if (c[m + 1] == c[m + 2] && c[m + 2] == c[m + 3]) {
(ans += e[i].val[now]) %= mod;
}
}
printf("%d\n", ans);
}
int main() {
// freopen("map.in", "r", stdin);
// freopen("map.out", "w", stdout);
n = read(); m = read();
inc[0] = 1; las = 1;
for (int i = 1; i <= m; ++i) inc[i] = inc[i - 1] << 3;
for (int i = 1; i <= n; ++i) {
scanf("%s", s + 1);
for (int j = 1; j <= m; ++j) {
if (s[j] == '?') a[i][j] = 0;
else if (s[j] == 'S') a[i][j] = 1, sig[1] = 1;
else if (s[j] == 'E') a[i][j] = 2, sig[2] = 1;
else if (s[j] == 'X') a[i][j] = 3, sig[3] = 1;
else if (s[j] == '.') a[i][j] = 4;
else if (s[j] == '#') a[i][j] = 5;
}
}
plug();
calc();
return 0;
}

浙公网安备 33010602011771号