Luogu P9169 [省选联考 2023] 过河卒 题解 [ 紫 ] [ 博弈论 ] [ BFS ] [ 模拟 ] [ 卡常 ]
过河卒:也是吃上伊思倍思的陈年老史了。
显然是一个有向图博弈论问题。如果某个点的出边有一个指向先手必败点,则这个点是先手必胜点;如果某个点的出边全都指向先手必胜点,则这个点是先手必败点;否则这个点是平局点。于是把所有终止状态丢进队列里,在反图上跑 BFS 即可。
进一步考虑如何最大化失败的步数 / 最小化成功的步数。观察 BFS 的实现,设两个点 \(u,v\) 满足 \(u\) 更新 \(v\) 的状态为先手必胜 / 必败点;不难发现当 \(v\) 为先手必胜点时,\(u\) 一定是第一个更新 \(v\) 的点;当 \(v\) 为先手必败点时,\(u\) 一定是最后一个更新 \(v\) 的点。恰好与题目中的限制相符,所以边 BFS 记录一下距离即可。
剩下的就是模拟建图了,细节很多,需要注意以下几点:
- 红子在四个方向上都能移动。
- 红子不能相撞。
- 黑子向上、左、右移动。
- 红子黑子都不能越界或者碰到障碍。
- BFS 是在反图上跑的。
- 多测清空。
终止状态有如下几种:
- 黑子在第一行,此时(红子的轮)先手必败。
- 红子黑子同位置,先手必败。
- 无法进行操作(出度为 \(0\)),先手必败。
有一些卡常技巧:
- 必须显式建图,否则在 BFS 的时候再枚举会显著增加常数。
- 加火车头。
- BFS 的时候给每个点分配编号。
- 手动实现队列。
- 剪掉无用或重复状态,例如钦定 \(rx_1 \le rx_2\),否则该状态剪掉。注意循环的时候也需要剪这一部分的枝,不然还是会 TLE。
- 开 C++23。
时间复杂度 \(O(Tn^3m^3)\)。写了 11KB,火车头是抄了 TLEWA 的。
#pragma GCC diagnostic push
#pragma G++ diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wattributes"
#pragma G++ diagnostic ignored "-Wpragmas"
#pragma G++ diagnostic ignored "-Wattributes"
#pragma G++ optimize(1)
#pragma G++ optimize(2)
#pragma G++ optimize(3)
#pragma G++ optimize("Ofast")
#pragma G++ optimize("inline")
#pragma G++ optimize("-fgcse")
#pragma G++ optimize("-fgcse-lm")
#pragma G++ optimize("-fipa-sra")
#pragma G++ optimize("-ftree-pre")
#pragma G++ optimize("-ftree-vrp")
#pragma G++ optimize("-ffast-math")
#pragma G++ optimize("-fsched-spec")
#pragma G++ optimize("-fdevirtualize")
#pragma G++ optimize("-fcaller-saves")
#pragma G++ optimize("-fschedule-insns")
#pragma G++ optimize("inline-functions")
#pragma G++ optimize("-ftree-tail-merge")
#pragma G++ optimize("-fschedule-insns2")
#pragma G++ optimize("-fstrict-aliasing")
#pragma G++ optimize("-fstrict-overflow")
#pragma G++ optimize("-fcse-skip-blocks")
#pragma G++ optimize("-fcse-follow-jumps")
#pragma G++ optimize("-fsched-interblock")
#pragma G++ optimize("-fpartial-inlining")
#pragma G++ optimize("no-stack-protector")
#pragma G++ optimize("-frerun-cse-after-loop")
#pragma G++ optimize("inline-small-functions")
#pragma G++ optimize("-finline-small-functions")
#pragma G++ optimize("-ftree-switch-conversion")
#pragma G++ optimize("-foptimize-sibling-calls")
#pragma G++ optimize("-findirect-inlining")
#pragma G++ optimize("-fexpensive-optimizations")
#pragma G++ optimize("-faggressive-loop-optimizations")
#pragma G++ optimize("inline-functions-called-once")
#pragma G++ optimize("-fdelete-null-pointer-checks")
#pragma G++ optimize("-fomit-frame-pointer")
#pragma G++ optimize("-fno-semantic-interposition")
#pragma G++ optimize("-freciprocal-math")
#pragma G++ optimize("tree-vectorize")
#pragma G++ optimize("move-loop-invariants")
#pragma G++ optimize("branch-target-load-optimize")
#pragma G++ optimize("btr-bb-exclusive")
#pragma G++ optimize("predictive-commoning")
#pragma G++ optimize("gcse-sm")
#pragma G++ optimize("gcse-las")
#pragma G++ optimize("ipa-pta")
#pragma G++ optimize("ipa-ra")
#pragma G++ optimize("ipa-cp")
#pragma G++ optimize("ipa-bit-cp")
#pragma G++ optimize("ipa-vrp")
#pragma G++ optimize("ipa-sra")
#pragma G++ optimize("prefetch-loop-arrays")
#pragma G++ optimize("-fmodulo-sched")
#pragma G++ optimize("-freschedule-modulo-scheduled-loops")
#pragma G++ optimize("-fselective-scheduling")
#pragma G++ optimize("-fsel-sched-pipelining")
#pragma G++ optimize("-fsel-sched-pipelining-outer-loops")
#ifdef __GNUC__
#define force_inline (__attribute__((always_inline)))
#else
#define force_inline [[gnu::always_inline]]
#endif
#include<bits/stdc++.h>
#pragma GCC target("sse3","sse2","sse","sse4","sse4.1","sse4.2","ssse3","f16c","fma","avx2","xop","fma4","mmx","popcnt","tune=native","abm")
#pragma G++ target("sse3","sse2","sse","sse4","sse4.1","sse4.2","ssse3","f16c","fma","avx2","xop","fma4","mmx","popcnt","tune=native","abm")
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 11, V = 2000005;
const int bgox[] = {1, 0, 0};
const int bgoy[] = {0, 1, -1};
const int rgox[] = {0, 0, 1, -1};
const int rgoy[] = {1, -1, 0, 0};
int n, m, du[V], q[V], hd, tl, cid[2][N][N][N][N][N][N];
char s[N][N];
int st[V], dis[V];
int h[V], idx;
struct Edge{
int v, ne;
}e[40000005];
void add(int u, int v)
{
e[++idx] = {v, h[u]};
h[u] = idx;
}
bool legal(int r, int bx, int by, int rx1, int ry1, int rx2, int ry2)
{
if(bx < 0 || rx1 < 0 || rx2 < 0) return 0;
if(by < 0 || ry1 < 0 || ry2 < 0) return 0;
if(bx >= n || rx1 >= n || rx2 >= n) return 0;
if(by >= m || ry1 >= m || ry2 >= m) return 0;
if(s[bx][by] == '#') return 0;
if(s[rx1][ry1] == '#') return 0;
if(s[rx2][ry2] == '#') return 0;
if(rx1 == rx2 && ry1 == ry2) return 0;
return 1;
}
void BFS()
{
while(tl - hd >= 0)
{
int u = q[hd++];
for(int i = h[u]; i ; i = e[i].ne)
{
int v = e[i].v;
if(st[v] != -1) continue;
if(st[u] == 0)
{
st[v] = 1;
dis[v] = dis[u] + 1;
q[++tl] = v;
}
else
{
du[v]--;
if(du[v] == 0)
{
st[v] = 0;
dis[v] = dis[u] + 1;
q[++tl] = v;
}
}
}
}
}
void solve()
{
cin >> n >> m;
idx = 0;
for(int i = 0; i <= 2 * n * m * n * m * n * m; i++)
{
st[i] = -1;
dis[i] = h[i] = du[i] = 0;
}
for(int i = 0; i < n; i++) cin >> s[i];
int curid = 0;
for(int r = 0; r <= 1; r++)
for(int bx = 0; bx < n; bx++)
for(int by = 0; by < m; by++)
for(int rx1 = 0; rx1 < n; rx1++)
for(int ry1 = 0; ry1 < m; ry1++)
for(int rx2 = rx1; rx2 < n; rx2++)
for(int ry2 = 0; ry2 < m; ry2++)
cid[r][bx][by][rx1][ry1][rx2][ry2] = curid++;
hd = 1;
tl = 0;
for(int r = 0; r <= 1; r++)
for(int bx = 0; bx < n; bx++)
for(int by = 0; by < m; by++)
for(int rx1 = 0; rx1 < n; rx1++)
for(int ry1 = 0; ry1 < m; ry1++)
for(int rx2 = rx1; rx2 < n; rx2++)
for(int ry2 = 0; ry2 < m; ry2++)
{
if(!legal(r, bx, by, rx1, ry1, rx2, ry2)) continue;
int id = cid[r][bx][by][rx1][ry1][rx2][ry2];
if(r == 0)
{
for(int i = 0; i < 4; i++)
{
int tx = rx1 + rgox[i], ty = ry1 + rgoy[i];
bool flag = 0;
if(tx > rx2)
{
swap(tx, rx2);
swap(ty, ry2);
flag = 1;
}
if(legal(r ^ 1, bx, by, tx, ty, rx2, ry2))
{
add(cid[r ^ 1][bx][by][tx][ty][rx2][ry2], id);
du[id]++;
}
if(flag)
{
swap(tx, rx2);
swap(ty, ry2);
}
}
for(int i = 0; i < 4; i++)
{
int tx = rx2 + rgox[i], ty = ry2 + rgoy[i];
bool flag = 0;
if(rx1 > tx)
{
swap(tx, rx1);
swap(ty, ry1);
flag = 1;
}
if(legal(r ^ 1, bx, by, rx1, ry1, tx, ty))
{
add(cid[r ^ 1][bx][by][rx1][ry1][tx][ty], id);
du[id]++;
}
if(flag)
{
swap(tx, rx1);
swap(ty, ry1);
}
}
}
else
{
for(int i = 0; i < 3; i++)
{
int tx = bx - bgox[i], ty = by + bgoy[i];
bool flag = 0;
if(rx1 > rx2)
{
swap(rx2, rx1);
swap(ry2, ry1);
flag = 1;
}
if(legal(r ^ 1, tx, ty, rx1, ry1, rx2, ry2))
{
add(cid[r ^ 1][tx][ty][rx1][ry1][rx2][ry2], id);
du[id]++;
}
if(flag)
{
swap(rx2, rx1);
swap(ry2, ry1);
}
}
}
if(bx == 0)
{
if(r == 0)
{
st[id] = dis[id] = 0;
q[++tl] = id;
}
continue;
}
if((bx == rx1 && by == ry1) || (bx == rx2 && by == ry2))
{
st[id] = dis[id] = 0;
q[++tl] = id;
continue;
}
if(du[id] == 0)
{
st[id] = dis[id] = 0;
q[++tl] = id;
}
}
BFS();
int bx, by, rx1 = -1, ry1 = -1, rx2, ry2;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(s[i][j] == 'X')
bx = i, by = j;
if(s[i][j] == 'O')
{
if(rx1 == -1)
rx1 = i, ry1 = j;
else
rx2 = i, ry2 = j;
}
}
}
if(rx1 > rx2)
{
swap(rx1, rx2);
swap(ry1, ry2);
}
int ans1 = st[cid[0][bx][by][rx1][ry1][rx2][ry2]], ans2 = dis[cid[0][bx][by][rx1][ry1][rx2][ry2]];
if(ans1 == -1) cout << "Tie\n";
else if(ans1 == 0) cout << "Black " << ans2 << "\n";
else cout << "Red " << ans2 << "\n";
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int id, t;
cin >> id >> t;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号