题解:P17429 [ICPC 2018 Xuzhou R] Rikka with Grid Graphs
逐点插入,维护一个轮廓线:若当前处理到点 \((i,j)\),则待更新的轮廓线包含第 \(i\) 行的前 \(j-1\) 个点和第 \(i-1\) 行的后 \(m-j+1\) 个点。
考虑维护将已处理的点的导出子图定向为 DAG 的方案数。设新插入的点为 \(u\),枚举连接 \(u\) 的边的方向,显然只有 \(u\) 的度数为 \(2\) 时才可能导致连出来的图有环。更具体地,设与 \(u\) 相连的两个点为 \(p,q\),我们只有连出 \(p\to u,u\to q\) 或者 \(q\to u,u\to p\) 才会形成一个环,对应的成环条件就是 \(q\leadsto p\) 或者 \(p\leadsto q\)。
于是将状态设为轮廓线上的 \(m\) 个点之间的可达关系。插入一条边 \(u\to v\) 时,取原图中满足 \(x\leadsto u\) 且 \(v\leadsto y\) 的 \(x,y\) 更新即可。可达关系可以直接压成 \(36\) 个 bit 塞到 unordered_map 里面。
分析状态数,考虑到合法的状态必然是 \(6\) 元偏序关系,可以搜出来方案数为 \(S=130023\)。而偏序关系还受网格性质限制,所以实际的状态数还要比这个 \(S\) 小。可以粗略估计时间复杂度为 \(\mathcal{O}(Tnm^2S)\)。
跑得不慢。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 6;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }
int tc, n, m;
unordered_map<ull, ll> dp, ndp;
int deg[MAXN][MAXN], in[MAXN][MAXN][2];
int f[MAXN + 1], g[MAXN + 1];
void ins(int u, int v, bool clr = true) {
if (clr) copy(f, f + m + 1, g);
for (int x = 0; x <= m; ++x) {
for (int y = 0; y <= m; ++y) {
if ((g[x] >> u & 1) && (g[v] >> y & 1)) g[x] |= 1 << y;
}
}
}
ull getSt(int p = -1) {
if (p != -1) {
swap(g[p], g[m]);
for (int i = 0; i < m; ++i) {
int s = g[i];
if ((s >> p & 1) != (s >> m & 1)) s ^= 1 << p;
g[i] = s & ((1 << m) - 1);
}
}
ull res = 0;
for (int x = 0; x < m; ++x) res |= (ull)g[x] << x * m;
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> tc;
while (tc--) {
cin >> n >> m;
for (int i = 0; i < n; ++i) fill(deg[i], deg[i] + m, 0);
cin.ignore(1, '\n');
for (int i = 0; i < n; ++i) {
string s;
getline(cin, s);
for (int j = 0; j < m - 1 && j * 2 + 1 < s.size(); ++j) {
if (s[j * 2 + 1] == '-') in[i][j + 1][deg[i][j + 1]++] = j;
}
if (i == n - 1) break;
getline(cin, s);
for (int j = 0; j < m && j * 2 < s.size(); ++j) {
if (s[j * 2] == '|') in[i + 1][j][deg[i + 1][j]++] = j;
}
}
dp.clear();
ull tmp = 0;
for (int i = 0; i < m; ++i) tmp |= 1ull << i * m + i;
dp[tmp] = 1;
for (int j = 1; j < m; ++j) {
ndp.clear();
if (!deg[0][j]) continue;
for (auto [st, val] : dp) {
for (int x = 0; x < m; ++x) f[x] = (st >> (x * m)) & ((1 << m) - 1);
ins(j - 1, j);
ndp[getSt()] += val;
ins(j, j - 1);
ndp[getSt()] += val;
}
ndp.swap(dp);
}
for (int i = 1; i < n; ++i) {
for (int j = 0; j < m; ++j) {
ndp.clear();
for (auto [st, val] : dp) {
for (int x = 0; x < m; ++x) f[x] = (st >> (x * m)) & ((1 << m) - 1);
f[m] = 1 << m;
if (!deg[i][j]) {
copy(f, f + m + 1, g);
ndp[getSt(j)] += val;
} else if (deg[i][j] == 1) {
int p = in[i][j][0];
ins(p, m);
ndp[getSt(j)] += val;
ins(m, p);
ndp[getSt(j)] += val;
} else {
int p = in[i][j][0], q = in[i][j][1];
ins(p, m);
ins(q, m, false);
ndp[getSt(j)] += val;
ins(m, p);
ins(m, q, false);
ndp[getSt(j)] += val;
if (~f[q] >> p & 1) {
ins(p, m);
ins(m, q, false);
ndp[getSt(j)] += val;
}
if (~f[p] >> q & 1) {
ins(q, m);
ins(m, p, false);
ndp[getSt(j)] += val;
}
}
}
ndp.swap(dp);
}
}
ll ans = 0;
for (auto [st, val] : dp) ans += val;
cout << ans << '\n';
}
return 0;
}

浙公网安备 33010602011771号