[CF1521E]Nastia and a Beautiful Matrix

89 篇文章 0 订阅

题目

传送门 to CF

思路

显然是构造题,首先考虑绝对下界,也就是空格最少的情况。

一个空格能够让哪些位置为左上角的 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| maxcntRed+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)
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值