Codeforces 1294E Obtain a Permutation

Posted aemshana

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 1294E Obtain a Permutation相关的知识,希望对你有一定的参考价值。

题目大意

给定一个(N imes M) 的矩阵 ((1 leq N,M leq 2 imes 10^5 , N imes M leq 2 imes 10^5))

有两种操作,
操作一:选取任意一个元素,将它改变成任意值
操作二:选取某一列,将这一列的所有元素循环上移一格,如下图将第一列循环上移一格
技术图片

要求用最少的操作步数,使得矩阵变成如下形式
技术图片
输出最少的操作步数。

题解

我们用输入的矩阵减去最终的矩阵,可以得到一个增量矩阵,对应着原矩阵的每个元素还要变化多少,才能得到最终的矩阵。

容易发现,列与列之间互不干扰,每一列都可以独立计算出这一列的最少操作步数,所有列的最少操作步数相加后即得答案。

举个例子
不妨令(N=4,M=3),
则第一列一定是(1,4,7,10)
依次使用操作二,
(1,4,7,10) 移动0次
(10,1,4,7) 移动1次
(7,10,1,4) 移动2次
(4,7,10,1) 移动3次

每一列的增量矩阵依次变为
(0,0,0,0)
(9,-3,-3,-3)
(6,6,-6,-6)
(3,3,3,-9)
可以发现,增量矩阵中的每个元素一定是(M)的倍数,否则不符合要求,只能通过操作一去修改

把增量矩阵的每一个元素除以(M),得
(0,0,0,0)
(3,-1,-1,-1)
(2,2,-2,-2)
(1,1,1,-3)
得到的这个东西很有规律。

可以发现,如果我们把当前列的第(i)个元素移动到第一个位置上,需要移动(i)次,且这一列的增量矩阵除以(M)后只能有上面给出的两个数。

比如(2,2,-2,-2),需要移动(2)次,且前(2)个元素只能是(2),后两个元素只能是(-2)

再比如(1,1,1,-3) ,需要移动(3)次,且前(3)个元素只能是(1),后两个元素只能是(-3)

只要维护一个Cnt数组,对于每一列扫一遍它的增量矩阵,即可算出至少需要通过操作一改变几个元素才能再使用(i)次操作二得到最终的矩阵。

时间复杂度(O(MN))

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;

vector<int> Data[200010];
int Cnt[400010];
int N,M;

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

inline int Calc(int pos){
    for(register int i=0;i<N;++i){
        int x=Data[pos][i];
        if(x%M!=0) continue;
        x/=M;
        if(x>N-1 || x<-(N-1)) continue;
        if(x<=0 && i>=-x) ++Cnt[x+N-1];
        else if(x>0 && i<N-x) ++Cnt[x+N-1];
    }
    int Res=2147483647;
    for(register int i=0;i<=N-1;++i){
        int temp=(N-i)-Cnt[N-i-1]+i-Cnt[N-i+N-1]+i;
        Res=min(Res,temp);
    }
    for(register int i=0;i<2*N-1;++i)
        Cnt[i]=0;
    return Res;
}

int main(){
    Read(N);Read(M);
    for(register int i=1;i<=N;++i){
        for(register int j=1;j<=M;++j){
            int x;Read(x);
            Data[j].push_back(x-((i-1)*M+j));
        }
    }
    int Ans=0;
    for(register int i=1;i<=M;++i)
        Ans+=Calc(i);
    cout<<Ans<<endl;

    return 0;
}

以上是关于Codeforces 1294E Obtain a Permutation的主要内容,如果未能解决你的问题,请参考以下文章

[Codeforces #615 div3]1294E Obtain a Permutation

U - Obtain a Permutation CodeForces - 1294E

CF1294E Obtain a Permutation 题解

Codeforces Round #615 (Div. 3) E. Obtain a Permutation

Educational Codeforces Round 77 (Rated for Div. 2) B. Obtain Two Zeroes

Educational Codeforces Round 77 (Rated for Div. 2) B. Obtain Two Zeroes