「CF1521E」Nastia and a Beautiful Matrix - 題解
-
題目大意
你有 \(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} \]