https://codeforces.com/problemset/problem/1294/E
思路:
按每列进行处理。
对于每一列,我们如果暴力统计移动n次的结果必然超时。(我记得去年多校的签到也有这个trick cf某道1500的好像也是这样
优化就是,值不大(大了也可以离散化
对于每个数,可以得到他到这一列的对应位置应该是哪个。(题目的图也给了公式)
然后都丢到map里。遍历map,对于一个步数相同的,就算出剩下的(剩下的肯定都是不能到对应位置,所以只能换)的操作数,枚举出该列最小,然后全列累加。
一个细节: a[i][j]可以比较,导致超出本次test的n*m,导致pos的放入map出现问题。所以超出范围的conitnue不存就好
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e6+1000;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL ma[maxn];
LL n,m;
LL get(LL x,LL y){
return (x-1)*m+y;
}
int main(void){
cin.tie(0);std::ios::sync_with_stdio(false);
cin>>n>>m;
for(LL i=1;i<=n;i++){
for(LL j=1;j<=m;j++){
LL num;cin>>num;
ma[get(i,j)]=num;
}
}
LL ans=0;
///每列处理
for(LL j=1;j<=m;j++){
LL res=n;
map<LL,LL>map1;
for(LL i=1;i<=n;i++){
if( (ma[get(i,j)]-j)%m==0 ){
LL pos=(ma[get(i,j)]-j)/m+1;///该数字实际应该在的位置
if(pos<1||pos>n) continue;///a[i][j]可能很大超了
if(pos<=i){
map1[i-pos]++;
}
else if(pos>i){
LL step=(i-1)+(n-pos+1);
map1[step]++;
}
}
}
for(auto i:map1){
res=min(res,i.first+n-i.second);
}
ans+=res;
}
cout<<ans<<"\n";
return 0;
}