CodeForces 1521E Nastia and a Beautiful Matrix 题解
这道题有多解,下面给出一种无需二分的做法。
Hint 1
先假设已经求出了 $size$。观察题目所给条件,是否可以把网格分成两部分,使得每部分里互不影响,只需要考虑两部分之间的冲突?
Hint 2
按行的奇偶性来分,那么网格应当是这样的:11111
2 2 2
11111
2 2 2
11111
怎样安排数字,才能最大化利用上面Hint 1的性质?
Hint 3
存在一种安排方法,使得只有一种数字 $x$ 可能出现冲突。在放置 $x$ 时,会出现什么问题?如何避免?题解
假设 $size$ 为最小边长。考虑到 \(mat_{i,j} \neq mat_{i+1,j+1}\) 且 \(mat_{i,j+1} \neq mat_{i+1,j}\)。
我们将网格按行的奇偶性分成两部分 \(S_1\), \(S_2\)。
11111
2 2 2
11111
2 2 2
11111
发现,每部分里的数字两两不影响,只有两部分间才可能出现冲突。
然后,我们如果把所有相同的数字放在一起,按某种顺序排好,先填 \(S_1\), 再填 \(S_2\),可以做到,只有1种数字 \(x\) 同时在 \(S_1\) 与 \(S_2\) 中出现。理解一下,因为数字是一段一段的,肯定只有一段从中间被分开了。
顺便算出 \(x\) 留在 \(S_1\) 中的个数 \(num\) (则在 \(S_2\) 中的有 \(cnt_{x} - num\) 个)。
为了不冲突,把\(S_1\) 里的放在下图 \(A\) 处,把\(S_2\)里的放在 \(B\) 处。
A A A
B B B
A A A
B B B
A A A
这会带来新的问题:\(S_1\) 里最多只能放 \(lim = (\left\lceil\dfrac{size}{2}\right\rceil)^2\) 个,可能不够放。为了避免,我用的方法是:把 \(a_i\) 排序,按 \(a_i\) 的降序填数。
简单证明一下,设 \(n \geq 2\) ,\(cap = size \times \left\lceil\dfrac{size}{2}\right\rceil\) 为 \(S_1\) 的大小,则\(a_1 \geq a_2 \geq ... \geq a_n\)。显然,\(a_1\) 对应的数字不会成为 \(x\)。
假设 \(x\) 是 \(a_k\) 所对应的,则 \(sum1 = a_1 + a_2 + ... + a_{k-1} \leq cap\),\(sum2 = a_1 + a_2 + ... + a_k > cap\)。因此 \(a_k \leq \dfrac{cap}{k}\)。而 \(num < a_k\)。
\(k = 2\) 时,\(num\) 最大值可达 \(\dfrac{cap}{2}\),显然 \(lim \geq \dfrac{cap}{2}\) 。可见 \(lim\) 恒不小于 \(num\)。
剩下的数字呢?\(x\) 前面的都放 \(S_1\),后面的都放 \(S_2\)。
求 \(size\) 的方法还没说呢。上述构造方法可以保证,满足下面两个条件的输入都能找到解。
1.\(m \leq size^2 - (\left\lfloor\dfrac{size}{2}\right\rfloor)^2\)
2.\(max{a_i} \leq cap\)
枚举即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
#define check(x) if(x>=mod) x-=mod
const int inf=0x3f3f3f3f,mod=1000000007;
const double pi=3.1415926535897932,eps=1e-6;
void chmax(int &x,int y){if(x < y) x = y;}
void chmin(int &x,int y){if(x > y) x = y;}
int qpow(int x,int y){
int ret = 1;
while(y) {
if(y & 1) ret = (ll)ret * x % mod;
x = (ll)x * x % mod;
y >>= 1;
}
return ret;
}
int n,m,mx,ans[1005][1005];pii a[100005];
void solve()
{
scanf("%d%d",&m,&n);
rep(i,1,n) scanf("%d",&a[i].fi),a[i].se = i;
sort(a+1,a+n+1);reverse(a+1,a+n+1);
mx = 1;rep(i,2,n) if(a[mx] < a[i]) mx = i;
int s = 0;while(s * ((s + 1) / 2) < a[mx].fi || (s * s) - ((s / 2) * (s / 2)) < m) s++;
int cap = s * ((s + 1) / 2);
int id = n + 1, rest = 0;rep(i,1,n) if(rest + a[i].fi > cap) {id = i;break;} else rest += a[i].fi;
int u = 1, v = 1;
int num = cap - rest;
if(id <= n)rep(i,1,num) {
ans[u][v] = a[id].se;
v += 2;
if(v > s) v = 1, u += 2;
}
u = 1;v = 1;
rap(i,1,id) {
while(a[i].fi){
if(!ans[u][v]) ans[u][v] = a[i].se, a[i].fi--;
v++;
if(v > s) v = 1, u += 2;
}
}
u = 2;v = 1;
if(id <= n) rep(i,1,a[id].fi - num) {
ans[u][v] = a[id].se;
v += 2;
if(v > s) v = 1, u += 2;
}
rep(i,id+1,n) {
while(a[i].fi){
if(!ans[u][v]) ans[u][v] = a[i].se, a[i].fi--;
v += 2;
if(v > s) v = 1, u += 2;
}
}
printf("%d\n",s);
rep(i,1,s) rep(j,1,s) printf("%d%c",ans[i][j],j==s?'\n':' '),ans[i][j] = 0;
}
int main()
{
int T;scanf("%d",&T);while(T--) solve();return 0;
}