题解:P14036 [PAIO 2025] Rooks

一种新做法,荣获本题最劣解。

根据题意稍加转化我们可以得到,设要去到值为 \(v\) 的点,当前点的值为 \(u\),一定有 \(u<v\le u+K\)

先考虑朴素的暴力,发现瓶颈在于对于每个点,我们需要找到合法的目标点,接着我们考虑到跟具体的格子没有关系,最后把答案还原一下即可,所以我们把每行的值和每列的值分别排序。接着我们在找合法点的时候就可以二分了,优化到 \(O(nm\log (n+m)K)\),还是过不了。

我们又发现瓶颈在于可能又会重复加入同一个点的情况,我们动态记录每个点下一个未访问的格子,维护可以用一种类似并查集的路径压缩的东西。讲并查集看做一个 \(\log\),复杂度 \(O(nm \log^2 (n+m))\)

不会写交互写完大体框架交给 AI 优化了一下,所以可读性可能比较低,但是我给加了注释。

#include<bits/stdc++.h>
using namespace std;
int pr[6250005],pc[6250005],d[6250005],q[6250005];          // pr:数字->行 pc:数字->列 d:距离 q:队列
int ar[2505][2505],ac[2505][2505],fr[2505][2505],fc[2505][2505]; // ar:行排序 ac:列排序 fr:行并查集 fc:列并查集
int gr(int r,int x){return fr[r][x]==x?x:fr[r][x]=gr(r,fr[r][x]);} // 行并查集查找(路径压缩)
int gc(int c,int x){return fc[c][x]==x?x:fc[c][x]=gc(c,fc[c][x]);} // 列并查集查找(路径压缩)
vector<vector<int>> calculate_moves(vector<vector<int>> A,int K){
	int n=A.size(),m=A[0].size(),ql=0,qr=0;                  // n:行数 m:列数 ql:队头 qr:队尾
	for(int i=0;i<=n*m;i++)d[i]=-1;                          // 初始化距离数组(-1表示不可达)
	for(int i=0;i<n;i++)for(int j=0;j<m;j++){                // 遍历矩阵
		int v=A[i][j];pr[v]=i;pc[v]=j;ar[i][j]=v;ac[j][i]=v; // 记录数字位置,填充行列数组
	}
	for(int i=0;i<n;i++){sort(ar[i],ar[i]+m);for(int j=0;j<=m;j++)fr[i][j]=j;} // 行排序+并查集初始化
	for(int j=0;j<m;j++){sort(ac[j],ac[j]+n);for(int i=0;i<=n;i++)fc[j][i]=i;} // 列排序+并查集初始化
	d[1]=0;q[qr++]=1;                                        // 起点(数字1)入队,步数为0
	while(ql<qr){                                             // BFS主循环
		int u=q[ql++];int r=pr[u],c=pc[u];                    // 取出队首数字u及其行列
		int i=lower_bound(ar[r],ar[r]+m,u+1)-ar[r];           // 行二分:找第一个>u的位置
		for(int x=gr(r,i);x<m&&ar[r][x]<=u+K;x=gr(r,x)){      // 遍历行中所有可到达的数字
			int v=ar[r][x];if(d[v]==-1){d[v]=d[u]+1;q[qr++]=v;} // 未访问则更新距离并入队
			fr[r][x]=x+1;                                      // 删除当前格子(指向下一个)
		}
		int j=lower_bound(ac[c],ac[c]+n,u+1)-ac[c];           // 列二分:找第一个>u的位置
		for(int x=gc(c,j);x<n&&ac[c][x]<=u+K;x=gc(c,x)){      // 遍历列中所有可到达的数字
			int v=ac[c][x];if(d[v]==-1){d[v]=d[u]+1;q[qr++]=v;} // 未访问则更新距离并入队
			fc[c][x]=x+1;                                      // 删除当前格子(指向下一个)
		}
	}
	vector<vector<int>> R(n,vector<int>(m));                  // 构造结果矩阵
	for(int i=0;i<n;i++)for(int j=0;j<m;j++)R[i][j]=d[A[i][j]]; // 按原矩阵形状填入步数
	return R;
}
posted @ 2026-02-20 15:57  Cefgskol  阅读(0)  评论(0)    收藏  举报