[构造] Codeforces 1276C/1278E Beautiful Rectangle

题目大意

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

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

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

\(n\leq 4 \times 10^5\)

题解

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

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

那么我们用一个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=\left\lfloor\frac{Cnt}{Row}\right\rfloor\times Row\),但是\(Col\)不能小于\(Row\),不然就不符合我们之前规定的列数大于等于行数。可以证明一定存在一种填法,能够填满这\(Row \times Col\)的矩形。

所以我们可以先把所有数按出现的次数排序,再用\(O\left(\sqrt nlogn\right)\)的时间枚举行数并计算出最大的矩形的大小,接着再用\(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("\n");
    }
    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\n%d %d\n",Total,Row,Col);
    Maintain();
    return 0;
}
上一篇:hiho一下:Beautiful String


下一篇:Python里的17个超赞操作,让你的技法更beautiful