「清華集訓2014」簡單迴路-插頭DP
阿新 • • 發佈:2018-12-22
Description
Solution
直接做 次插頭DP得分為 。
考慮題目中只詢問了豎直的邊,所以考慮分別做一遍前後綴插頭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;
}