CodeForces 1521E Nastia and a Beautiful Matrix 题解

CF1521E链接

这道题有多解,下面给出一种无需二分的做法。

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;
}
posted @ 2021-05-09 11:51  beacon_cwk  阅读(121)  评论(0编辑  收藏  举报