[GDKOI2016]地图

前话:这可能是我做的所有题中时间跨度最大的题了,5.4-5.13,因为中间停竞赛了(

Description

给定一个 \(n*m\) 的地图,每个地方可能是平地,障碍物,出口,入口,神器或未知。现在你要求有多少种地图,使得它满足:

  1. 入口,出口和神器均出现一次且不在同一个格子里;

  2. 上述三者两两四联通。

答案对 \(10 ^ 9 + 7\) 取模。

\(1\leq n,\ m \leq 7\)

Analysis

首先你有一个 \(O(5 ^ {nm})\) 的算法,然后加个特判条件 1 就可以优化到大概 \(O(2 ^{nm})\) 了。

(好吧我在说废话)

仔细想想:方格图,连通块,\(7*7\)(指比较小),方案数。

我们发现这到题无论从题目背景还是数据范围都很插头 DP 。

哟西,开干,不想做了溜了

Solution

连通块的类似问题我们是已经见识过的了,用最小表示法维护一整行的联通情况。

由于一行最多 \(7\) 个格子,最多会有 \(4\) 个单独的连通块,加上不属于任何连通块的一种,再怎么着 \(8\) 进制状态也是够了的。

但是好像有些特殊限制,那我们可以在末尾加三个位置,专门记录入口。出口和神器处在的连通块。

然后最统计答案的话,按照题目要求,只要全部属于一个连通块就行。

那现在只剩下转移了哈哈。

不过意外的发现转移分讨量小的惊人:

  1. 障碍物直接把原位置变成零就能插入;

  2. 其他非问号该是啥是啥;

  3. 问号分成上面两种情况做就行。

然后 \(2,\ 3\) 情况插入非问号的时候注意连通块的变化:

  1. 如果是入口,出口或神器要判断是否已经插入过,有的话就当作什么事没发生,默默跳过;

  2. 如果左边和上边都不属于连通块,新开一个连通块;

  3. 如果仅左边有连通块或仅上边有连通块,谁有就继承谁的;

  4. 都有的话就把全部属于两个连通块的格子变成其中一个。

结束之后记得如果插入的是入口,出口或神器要把单独拎出来的位置也标记好。

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;
}

posted @ 2022-05-13 20:37  Illusory_dimes  阅读(22)  评论(1编辑  收藏  举报