QOJ-6272 克莱因瓶
QOJ-6272 克莱因瓶
给定一个 \((n+1)\times(n+1)\) 的网格图,每个点坐标为 \((i,j)\),其中 \(0\le i,j\le n\)。对于每个 \((i,j)\):
- 如果 \(j\ge 1\),则到 \((i,j-1)\) 有一条双向边,以及 \((i,0)\) 到 \((i,n)\) 有一条双向边。
- 如果 \(i\ge1\),则到 \((i-1,j)\) 有一条双向边,以及 \((0,j)\) 到 \((n,n-j)\) 有一条双向边。注意这里 \(\boldsymbol{j}\) 连接到 \(\boldsymbol{n-j}\),所以你可以理解成一个克莱因瓶。
每次给定克莱因瓶上的两个点,问它们之间的最短路长度。\(1\le n\le10^8\),\(1\le q\le10^5\)。
小清新思维题。图的结构示意如下。
结论:最多只会利用一次 \((0,j)\leftrightarrow(n,n-j)\) 的边。
感性理解,如果利用 \(\ge2\) 次这种边,则还不如不用,直接在网格上走即可。在上图中模拟不难看出。
因此分类讨论,有如下几种情况。
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, q; cin >> n >> q;
while (q--) {
int i1, j1, i2, j2;
cin >> i1 >> j1 >> i2 >> j2;
vector<tuple<int, int, int>> v({
{0, n - j1, n - i1 + 1},
{n, n - j1, i1 + 1},
{i1, j1, 0}
});
int ans = n * 2;
for (auto [i, j, c]: v) {
int di = abs(i - i2), dj = abs(j - j2);
ans = min(ans, c + di + min(dj, n + 1 - dj));
}
cout << ans << '\n';
}
}
signed main() {
cin.tie(0)->sync_with_stdio(0);
int tt = 1;
// cin >> tt;
while (tt--) solve();
}

浙公网安备 33010602011771号