zoukankan      html  css  js  c++  java
  • [构造] Codeforces 1276C/1278E Beautiful Rectangle

    题目大意

    我们称一个矩阵是美丽的,当且仅当该这矩阵中不存在两个相同的数在同一列或在同一行。

    给定 (n) 个数,要求你选出尽量多的数,使它们能够组成一个美丽的矩形。

    注意,本题要求输出选出的数的个数与组成矩形大小和具体方案。

    (nleq 4 imes 10^5)

    题解

    想了好几天了,终于想出来了,果然是我太菜...

    首先,可以想到,对于一个(x)行的矩形(令列数大于等于行数),如果有一个数出现了超过(x)次,是不可能将这个数全部填入这个矩形的,最多只能填(x)个,即斜着填,按下图的顺序。

    那么我们用一个map维护每个数出现的次数,按出现的次数从大到小来填数,相同的数填在一起。为什么要按出现的次数从大到小来填呢?如果随便填的话,会被这组数据hack掉。

    7
    8 5 10 4 10 8 3
    

    我的答案是

    6
    2 3
    3 5 8
    10 4 8
    

    正确答案是

    6
    2 3
    8 10 3
    4 8 10
    

    发现虽然是斜着填的,但两个8竟然在同一列。可以证明,按出现的次数从大到小来填数,就不会出现这样的情况。

    然后我们就要先确定这个矩形有多少行,我们从(1 sim sqrt n)去枚举行数,假设当前枚举到的行数为(Row),那么对于每个数,我们最多取(Row)次,可以用前缀和+二分计算出我们总共能取的数的数量(Cnt),然后就可以计算出行数(Col=leftlfloorfrac{Cnt}{Row} ight floor imes Row),但是(Col)不能小于(Row),不然就不符合我们之前规定的列数大于等于行数。可以证明一定存在一种填法,能够填满这(Row imes Col)的矩形。

    所以我们可以先把所有数按出现的次数排序,再用(Oleft(sqrt nlogn ight))的时间枚举行数并计算出最大的矩形的大小,接着再用(O(n))的时间去填数,最终的时间复杂度为(O( nlogn + sqrt nlogn +n))

    Code

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #include <map>
    using namespace std;
    
    #define RG register int
    #define LL long long
    
    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);
    }
    
    struct Node{int Num,Cnt;};
    map<int,int> Hash;
    vector<Node> Com;
    vector<int> Data;
    vector<int> Mat[1000];
    
    int Sum[400010];
    int N,Row=0,Col=0,Total=0;
    
    bool cmp(Node A,Node B){return A.Cnt>B.Cnt;}
    
    inline int Judge(int row){
        int Pos=upper_bound(Data.begin(),Data.end(),row)-Data.begin()-1;
        int Res=(Sum[Pos]+((int)Data.size()-Pos-1)*row)/row*row;
        if(Res/row<row) return 0;
        return Res;
    }
    
    int CountY=0;
    
    inline void Move(int &px,int &py){
        if(px==Row-1) ++CountY;
        if(px+1<Row && py+1<Col){++px;++py;return;}
        if(px+1>=Row && py+1<Col){px=0;py=CountY;return;}
        if(px+1<Row && py+1>=Col){py=0;++px;return;}
        if(px+1>=Row && py+1>=Col){px=0;py=CountY;return;}
        return;
    }
    
    inline void Maintain(){
        int px=0,py=0;
        for(RG i=1;i<=Col;++i)
            for(RG j=0;j<Row;++j)
                Mat[j].push_back(0);
        for(int i=0;i<(int)Com.size();++i){
            int Num=Com[i].Num,Cnt=min(Com[i].Cnt,Row);
            for(RG i=1;i<=Cnt;++i){
                Mat[px][py]=Num;
                Move(px,py);
            }
        }    
        for(RG i=0;i<Row;++i){
            for(RG j=0;j<Col;++j){
                printf("%d",Mat[i][j]);
                if(j<Col-1) printf(" ");
            }
            printf("
    ");
        }
        return;
    }
    
    int main(){
        Read(N);
        for(RG i=1;i<=N;++i){
            int x;Read(x);
            ++Hash[x];
        }
        Data.push_back(-2147483647);
        for(auto it:Hash){
            Data.push_back(it.second);
            Com.push_back((Node){it.first,it.second});
        }
        sort(Com.begin(),Com.end(),cmp);
        sort(Data.begin(),Data.end());
        for(RG i=1;i<(int)Data.size();++i)
            Sum[i]=Sum[i-1]+Data[i];
        for(RG i=1;i*i<=N;++i){
            int temp=Judge(i);
            if(temp>Total){Total=temp;Row=i;}
        }
        Col=Total/Row;
        printf("%d
    %d %d
    ",Total,Row,Col);
        Maintain();
        return 0;
    }
    
  • 相关阅读:
    1833: [ZJOI2010]count 数字计数——数位dp
    【模板】BZOJ 3685: 普通van Emde Boas树——Treap
    【模板】解决二分图匹配的强力算法——Hopcroft-Karp算法
    BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集
    【模板】二分图匹配/一般图匹配——匈牙利算法/随机匈牙利算法
    【模板】BZOJ 1692:队列变换—后缀数组 Suffix Array
    BZOJ 4241: 历史研究——莫队 二叉堆
    【模板】BZOJ 3781: 小B的询问 莫队算法
    BZOJ 3656: 异或 (组合数取模 CRT)
    【模板】SPOJ FACT0 大数分解 miller-rabin & pollard-rho
  • 原文地址:https://www.cnblogs.com/AEMShana/p/12389152.html
Copyright © 2011-2022 走看看