zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    两款开发辅助工具介绍
    探究Repository模式的两种写法与疑惑
    js 时间处理
    Jquery元素追加和删除
    js 格式验证总结
    jquery UI datepicker时间控件的使用
    jquery 实现 点击按钮后倒计时效果,多用于实现发送手机验证码、邮箱验证码
    JS 字符串编码函数(解决URL特殊字符传递问题):escape()、encodeURI()、encodeURIComponent()区别详解
    form表单和ajax表单提交(Html.BeginForm()、Ajax.BeginForm())的差别
    了解了这些才能开始发挥jQuery的威力(转)
  • 原文地址:https://www.cnblogs.com/AEMShana/p/12234339.html
Copyright © 2011-2022 走看看