「清华集训2014」简单回路-插头DP

Description

链接

Solution

直接做 Q Q 次插头DP得分为 60 60

考虑题目中只询问了竖直的边,所以考虑分别做一遍前后缀插头DP,记录下每一行的每种轮廓线对应的方案数,询问时合并轮廓线。

两条轮廓线互补相当于把轮廓线上的边连起来能形成一个环,用并查集判就行了。

#include <bits/stdc++.h>
using namespace std;

typedef long long lint;
const int maxn = 1005, maxm = 10, mod = 1e9 + 7;

int n, m, k, g[maxn][maxm];
unordered_map<int, int> f[2], pre[maxn], suf[maxn];
int fa[maxn], q1[maxn], q2[maxn];

inline int gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	int sum = 0;
	while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

inline void inc(int &a, int b) {a += b; if (a >= mod) a -= mod;}

int find(int x)
{
	if (fa[x] == x) return x;
	return fa[x] = find(fa[x]);
}

void solve(int d, unordered_map<int, int> sum[maxn])
{
	int now = 0, s, b1, b2, p1, p2, v;
	unordered_map<int, int>::iterator it;
	f[0].clear(); f[1].clear();
	f[1][0] = 1;
	for (int i = d == 1 ? 1 : n; i && i <= n; i += d) {
		for (int j = 1; j <= m; ++j) {
			if (j == 2) swap(f[now], sum[i - d]);
			f[now].clear(); now ^= 1;
			for (it = f[now].begin(); it != f[now].end(); ++it) {
				s = j == 1 ? (it -> first << 2) : it -> first;
				p1 = (j - 1) << 1; p2 = j << 1;
				b1 = (s >> p1) & 3; b2 = (s >> p2) & 3;
				s ^= b1 << p1 | b2 << p2;
				v = it -> second;
				if (!g[i][j]) {
					if (!b1 && !b2) inc(f[now ^ 1][s], v);
				} else if (!b1 && !b2) {
					if (g[i + d][j] && g[i][j + 1]) inc(f[now ^ 1][s | 1 << p1 | 2 << p2], v);
					inc(f[now ^ 1][s], v);
				} else if (!b1 || !b2) {
					if (g[i + d][j]) inc(f[now ^ 1][s | (b1 | b2) << p1], v);
					if (g[i][j + 1]) inc(f[now ^ 1][s | (b1 | b2) << p2], v);
				} else if (b1 == 1 && b2 == 1) {
					int cnt = 1;
					for (int k = j + 1; k <= m; ++k) {
						if ((s >> (k << 1) & 3) == 1) ++cnt;
						if ((s >> (k << 1) & 3) == 2) --cnt;
						if (cnt == 0) {
							inc(f[now ^ 1][s ^ (3 << (k << 1))], v);
							break;
						}
					}
				} else if (b1 == 2 && b2 == 2) {
					int cnt = 1;
					for (int k = j - 2; k >= 1; --k) {
						if ((s >> (k << 1) & 3) == 1) --cnt;
						if ((s >> (k << 1) & 3) == 2) ++cnt;
						if (cnt == 0) {
							inc(f[now ^ 1][s ^ (3 << (k << 1))], v);
							break;
						}
					}
				} else if (b1 == 2 && b2 == 1) inc(f[now ^ 1][s], v);
			}
		}
	}
	swap(sum[d == 1 ? n : 1], f[now ^ 1]);
}

int main()
{
	n = gi(); m = gi(); k = gi();
	for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) g[i][j] = 1;
	for (int i = 1; i <= k; ++i) g[gi()][gi()] = 0;

	solve(1, pre);
	solve(-1, suf);

	int T = gi(), a, b, k, cnt1, cnt2, s, ans, x, y;
	unordered_map<int, int>::iterator it1, it2;
	while (T--) {
		x = gi(); y = (gi() - 1) << 1; ans = 0;
		for (it1 = pre[x].begin(); it1 != pre[x].end(); ++it1) {
			if (!((a = it1 -> first) >> y & 3)) continue;
			for (it2 = suf[x + 1].begin(); it2 != suf[x + 1].end(); ++it2) {
				if (!((b = it2 -> first) >> y & 3)) continue;
				for (k = 0; k < m; ++k)
					if ((!(a >> (k << 1) & 3)) ^ (!(b >> (k << 1) & 3))) break;
				if (k < m) continue;
				for (k = cnt1 = cnt2 = s = 0; k < m; ++k)
					if ((a >> (k << 1) & 3) || (b >> (k << 1) & 3)) {
						fa[k] = k; ++s;
						if (a >> (k << 1) & 1) q1[++cnt1] = k;
						if (a >> (k << 1) & 2) fa[find(q1[cnt1--])] = find(k), --s;
						if (b >> (k << 1) & 1) q2[++cnt2] = k;
						if (b >> (k << 1) & 2) {
							if (find(q2[cnt2]) != find(k)) fa[find(q2[cnt2])] = find(k), --s;
							--cnt2;
						}
					}
				if (s == 1) inc(ans, (lint)it1 -> second * it2 -> second % mod);
			}
		}
		printf("%d\n", ans);
	}
	
	return 0;
}


猜你喜欢

转载自blog.csdn.net/DSL_HN_2002/article/details/84973423