1. 程式人生 > >「清華集訓2014」簡單迴路-插頭DP

「清華集訓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; }