题目
思路
显然是构造题,首先考虑绝对下界,也就是空格最少的情况。
一个空格能够让哪些位置为左上角的 2 × 2 2\times 2 2×2 子矩形满足条件一呢?显然就是以它为右下角的 2 × 2 2\times 2 2×2 矩形。那么这些矩形两两不重叠的时候,显然是空格数量最小的时候。手玩可以发现,将 ( 2 k 1 , 2 k 2 ) (2k_1,2k_2) (2k1,2k2) 的格子(行和列均从 1 1 1 开始标号)都设置为空格就可以了。
我们只要证明,在这种情形下,所有格子都可以使用,那就可以直接找这个下界。而这是很简单的——首先,将图二染色,使得主对角线(包含所有 ( x , x ) (x,x) (x,x) 的格子)是黑色。称 “左上 - \text- -右下” 和 “左下 - \text- -右上” 为相邻关系。不难发现,黑色格子没有任何相邻关系,因为已经被空格隔开了。
白格呢?将图旋转一下,发现是一个类棋盘(上下左右连边)。将这个类棋盘再二染色一次,得到红色和绿色,不难发现,红色格子不相邻,绿色格子不相邻。那么,一种数字要是只在红色、绿色中的某一个出现,它就不会导致矩阵不合法。
这是两个背包吗?假设一个背包爆了,另一个背包直接硬塞,百分百有解啊!或者更 “构造” 一点:按照顺序填 R e d , B l a c k , G r e e n Red,Black,Green Red,Black,Green,由于 ∣ R e d ∣ = ∣ G r e e n ∣ |Red|=|Green| ∣Red∣=∣Green∣,所以只要 max c n t ≤ ∣ R e d ∣ + ∣ B l a c k ∣ \max cnt\le|Red|+|Black| maxcnt≤∣Red∣+∣Black∣,然后按照 c n t cnt cnt 从大到小填。(其实只要把 c n t cnt cnt 最大的一个特殊填就行了。)
所以我们就做完了。因为我们使得每个格子都能发挥作用。复杂度
O
(
m
+
k
)
\mathcal O(m+k)
O(m+k) 。然而我打了排序。
代码
#include <bits/stdc++.h>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 100005;
const int SqrtN = 500;
struct Node{
int cnt, val;
bool operator < (const Node &t) const {
return cnt > t.cnt;
}
};
Node num[MaxN];
int maze[SqrtN][SqrtN];
int main(){
for(int T=readint(); T; --T){
int m = readint(), k = readint();
rep(i,1,k){
num[i].cnt = readint();
num[i].val = i;
}
sort(num+1,num+k+1); int n = 1;
while(n*n-(n>>1)*(n>>1) < m) ++ n;
while((n>>1)*((n+1)>>1) // red
+(n*n+1)/2 // black
-(n>>1)*(n>>1) < num[1].cnt)
++ n; // increase
/* step one: tackle one num */ ;
for(int i=2; i<=n; i+=2)
for(int j=1; j<=n&&num[1].cnt; j+=2)
maze[i][j] = num[1].val, -- num[1].cnt;
for(int i=1; i<=n; i+=2)
for(int j=1; j<=n&&num[1].cnt; j+=2)
maze[i][j] = num[1].val, -- num[1].cnt;
/* step two: fill in all num */ ;
int p = 2; // which num is dealt with
for(int i=2; i<=n; i+=2)
for(int j=1; j<=n&&p<=k; j+=2){
if(maze[i][j]) continue;
for(; !num[p].cnt; ++p)
if(p == k+1) break;
if(p != k+1){
maze[i][j] = num[p].val;
-- num[p].cnt;
}
}
for(int i=1; i<=n; i+=2)
for(int j=1; j<=n&&p<=k; j+=2){
if(maze[i][j]) continue;
for(; !num[p].cnt; ++p)
if(p == k+1) break;
if(p != k+1){
maze[i][j] = num[p].val;
-- num[p].cnt;
}
}
for(int i=1; i<=n; i+=2)
for(int j=2; j<=n&&p<=k; j+=2){
for(; !num[p].cnt; ++p)
if(p == k+1) break;
if(p != k+1){
maze[i][j] = num[p].val;
-- num[p].cnt;
}
}
printf("%d\n",n);
/* step three: output */ ;
for(int i=1; i<=n; ++i,puts(""))
for(int j=1; j<=n; ++j){
printf("%d ",maze[i][j]);
maze[i][j] = 0; // clear
}
}
return 0;
}
/*
black = (n*n+1)>>1
blank = (n>>1)*(n>>1)
free = black - blank
red = (n>>1)*((n+1)>>1)
*/