YunYan

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

题解:

注意每一列与每一列之间互不影响,所以贪心地求出没一列的最小操作值,然后累加起来。

怎么求没一列的最小值呢?维护一个数组same表示其中same[i]=j表示将该序列向上翻滚i次有j个元素归位,那么会有n-j个没有归位,所以我们要修改他们,一共修改n-j次,所以总计n-j+i次。

所以每一列的答案为min(n-same[i]+i);关于same的求法。首先每一列的元素的取值范围是j<=arr[i][j]<=n*m,并且arr[i][j]%m==j%m,即每一列的元素值对m取值应该相等。只有这样才是该列的元素。

对于那些不满足条件的不用考虑。因为无论翻转多少次,都无法使其归位。对于满足条件的,假设当前位置为j,然后其目标位置是pos.那么答案为(j-pos+n)%n即为翻转的次数。pos的取值:(arr[i][j]-j)/m+1;

所以same[(j-pos+n)%n]++;

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2E5+7;
vector<ll >ve[N];
ll same[N];//元素i向上移动same[j]个可以恢复
int main()
{
    ll n,m;
    cin>>n>>m;
    ll c=n*m;
    ll y;
    for(ll i=1;i<=c;i++){
        cin>>y;
        if(i%m==0){
            ve[m].push_back(y);
        }
        else ve[i%m].push_back(y);
    }
    ll sum=0;
    for(ll i=1;i<=m;i++){
        ll c=ve[i].size();
        for(ll j=0;j<c;j++){
            if(ve[i][j]<i||ve[i][j]>n*m) continue ;
            if(ve[i][j]%m==i%m){
                ll pos=(ve[i][j]-i)/m+1;
                ll k=(j+1-pos+n)%n;
                same[k]++;            }
        }
        ll cnt=n;
        for(ll j=0;j<n;j++){
            cnt=min(cnt,j+n-same[j]);
            same[j]=0;
        }
        sum+=cnt;
    }
    cout<<sum<<endl;
    return 0;
}

 

posted on 2020-01-28 21:31  Target--fly  阅读(174)  评论(0编辑  收藏  举报