题解:P14036 [PAIO 2025] Rooks

如果暴力 BFS,枚举每个点之后暴力枚举同行同列的所有点,时间复杂度不会低于 \(O(nm(n+m))\),明显过不去,因此必须要以行和列为单位整体考虑。

由题意,路径上所有单元格的 \(A\) 值单调递增,因此类似于拓扑排序的思想,按照 \(A\) 值从小到大枚举单元格。假设枚举到单元格 \((r,c)\)。设 \(f_{r,c}\) 表示走到 \((r,c)\) 的最少步数。考虑对每行每列维护在该行/列中有哪些单元格可以进行有效转移,这个转移形如:

\[f_{r,c}=\min\limits_{(r',c')}\{f_{r',c'}\}+1 \]

其中 \((r',c')\) 满足 \(r'=r\lor c'=c\)\(A_{r,c}-K\le A_{r',c'}<A_{r,c}\)

发现一行/列中可以进行有效转移的单元格要么 \(f\) 值比较小,要么虽然 \(f\) 比较大,但是 \(A\) 比较小。换言之,这些单元格一定满足在 \(f\) 升序的情况下 \(A\) 降序的条件,可以使用单调队列维护。

每个单元格至多入队 \(2\) 次,且转移时直接取队首,时间复杂度 \(O(nm)\)

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(int x = (y); x <= (z); ++x)
#define per(x, y, z) for(int x = (y); x >= (z); --x)
#define endl '\n'
using namespace std;
typedef long long ll;

template<typename T> void chkmin(T& x, T y) {if(y < x) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

const int inf = 0x3f3f3f3f;

vector<vector<int>> calculate_moves(vector<vector<int>> A, int K) {
	int N = A.size(), M = A[0].size();
	rep(i, 0, N - 1) rep(j, 0, M - 1) --A[i][j];
	vector<int> r(N * M), c(N * M);
	rep(i, 0, N - 1) rep(j, 0, M - 1) r[A[i][j]] = i, c[A[i][j]] = j;
	vector<deque<int>> dqR(N), dqC(M);
	vector<int> dis(N * M, +inf);
	vector<vector<int>> R(N, vector<int>(M));
	dis[0] = 0;
	dqR[r[0]].push_back(dis[0]);
	dqC[c[0]].push_back(dis[0]);
	R[r[0]][c[0]] = 0;
	rep(i, 1, N * M - 1) {
		while(!dqR[r[i]].empty() && dqR[r[i]].front() < i - K) dqR[r[i]].pop_front();
		while(!dqC[c[i]].empty() && dqC[c[i]].front() < i - K) dqC[c[i]].pop_front();
		if(!dqR[r[i]].empty()) chkmin(dis[i], dis[dqR[r[i]].front()] + 1);
		if(!dqC[c[i]].empty()) chkmin(dis[i], dis[dqC[c[i]].front()] + 1);
		while(!dqR[r[i]].empty() && dis[dqR[r[i]].back()] >= dis[i]) dqR[r[i]].pop_back();
		while(!dqC[c[i]].empty() && dis[dqC[c[i]].back()] >= dis[i]) dqC[c[i]].pop_back();
		dqR[r[i]].push_back(i);
		dqC[c[i]].push_back(i);
		R[r[i]][c[i]] = dis[i] == +inf ? -1 : dis[i];
	}
	return R;
}

#ifdef LOCAL

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int N, M, K;
    cin >> N >> M >> K;
    vector<vector<int>> A(N, vector<int>(M));
    rep(i, 0, N - 1) rep(j, 0, M - 1) cin >> A[i][j];
    vector<vector<int>> R = calculate_moves(A, K);
    rep(i, 0, N - 1) rep(j, 0, M - 1) cout << R[i][j] << " \n"[j == M - 1];
    return 0;
}

#endif
posted @ 2025-10-04 21:03  rui_er  阅读(19)  评论(0)    收藏  举报