-
题目大意
你有 \(k\) 个数字,他们分别是 \(1,2,3,...,k\) 对于数字 \(i\) 你有相同的 \(a_i\) 个。
定义一个 \(n \times n\) 的矩阵为美丽矩阵:
-
这个 \(n\times n\) 的矩阵包含了你所拥有的所有数字;
-
对于每个 \(2\times 2\) 的子矩阵,占用的格子不能超过 \(3\)(当一个格子的数为 \(0\) 时,我们认为它没有被占用)并且每个对角线的数字是不同的;
现在,你要构造一个最小的美丽矩阵。
-
-
分析
首先,考虑每个 \(2\times2\) 矩阵都必须有一个 \(0\) ,不妨先计算出最少需要几个 \(0\) 。
显然,对于每个 \((i+j) \equiv 0 \pmod{2}\) 且 \(i\) 和 \(j\) 都为偶数时候,把 \((i,j)\) 赋值为 \(0\) ,此时的 \(0\) 数量最少。
所以最少需要的 \(0\) 的个数是 \(\left\lfloor \dfrac{p}{2} \right\rfloor ^2\) 。
所以在算 \(p\) 的时候,最小的 \(p\) 一定要满足 \(m>p^2-\left\lfloor \dfrac{p}{2} \right\rfloor ^2\) 。
接着我们对 \(0\) 的上下左右染色,上下为 \(1\) ,左右为 \(2\) 。
因为 \(0\) 此时已经满足题目要求了。所以我们考虑对角线要求。
此时只要考虑每组的 \(1\) 和 \(2\) 不同就好了。
但是,有可能此时染色无法满足要求。如果出现一个颜色的数量大于白色和 \(0\) (或 \(1\) )的总数,就无法满足要求。
但这种颜色最多存在 \(1\) 个,所以开头计算 \(p\) 的时候再多判断一下就好了。
如果保证一定有解,那就直接染色就好了。把 \(cnt_i\) 从小到大排序,\(0\) 从数量最小的颜色开始赋值,\(1\) 从最大的开始赋值。注意这里的赋值包括 \(0\) ,也优先考虑 \(0\) 。可以证明这样一定满足题目要求。
-
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e5+5,M=3000+5;
struct node{
int id,x;
}cnt[N];
int T,n,m,p,a[M][M],ans[M][M];
bool cmp(node u,node v)
{ if(u.x!=v.x)return u.x<v.x;
else return u.id<v.id;
}
void solve()
{
cnt[0].x=p*p-m;cnt[0].id=0;
for(int i=1;i<=p;i++)
for(int j=1;j<=p;j++)
a[i][j]=-1;
for(int i=1;i<=p;i++)
for(int j=1;j<=p;j++)
{
if((i&1)==0&&(j&1)==0&&((i+j)&1)==0)
{
a[i][j]=ans[i][j]=0;
cnt[0].x--;
a[i-1][j]=a[i+1][j]=1;
a[i][j-1]=a[i][j+1]=2;
}
}
int x=0,y=n;
while(x<=n&&cnt[x].x==0)x++;
while(y>=0&&cnt[y].x==0)y--;
for(int i=1;i<=p;i++)
for(int j=1;j<=p;j++)
{
if(a[i][j]==0)continue;
else if(a[i][j]==1)
{
ans[i][j]=cnt[x].id;
cnt[x].x--;
while(x<=n&&cnt[x].x==0)x++;
}
else if(a[i][j]==2)
{
ans[i][j]=cnt[y].id;
cnt[y].x--;
while(y>=0&&cnt[y].x==0)y--;
}
}
for(int i=1;i<=p;i++)
for(int j=1;j<=p;j++)
{
if(a[i][j]!=-1)continue;
ans[i][j]=cnt[x].id;
cnt[x].x--;
while(x<=n&&cnt[x].x==0)x++;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&cnt[i].x);
cnt[i].id=i;
}
sort(cnt+1,cnt+1+n,cmp);
if(m==1)
{
printf("1\n%d\n",cnt[n]);
continue;
}
p=2;
while(p*p-(p/2)*(p/2)<m)p++;
while(cnt[n].x>p*p-(p/2)*(p/2)-(p/2)*((p+1)/2))p++;
solve();
printf("%d\n",p);
for(int i=1;i<=p;i++)
{
for(int j=1;j<=p;j++)
printf("%d ",ans[i][j]);
printf("\n");
}
}
return 0;
}
\[\text{by Rainy7} \]