题解: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;
}

浙公网安备 33010602011771号