第一次考場切構造題
首先我們摸一下樣例
有一個顯然的結論
如果我們依次從左到右把 \(n\) 個數分成 \(\frac{n}{k}\) 份
並且每一份大小都為 \(k\)
那么每一個子集在每一組數中都恰好選一個
\[1.2.3 : 4.5.6 : 7.8.9 : 10.11.12 \]
所以,我們不妨把序列表示成:
\[1.2.3 : 1.2.3 : 1.2.3 : 1.2.3 \]
轉換成矩陣:
\[1-1-1-1 \]
\[2-2-2-2 \]
\[3-3-3-3 \]
那么題意就轉換成讓每一列上的數互換,求一種方案使得每一行的數字之和相等
樣例這種情況很好想,我么只要把 \(\frac{n}{2k}\) 列的數字 \(reverse\) 一下就好了
這種情況其實是針對 \(n/k\) 為偶數的情況的
考慮 \(n/k\) 為奇數的情況
容易發現我們只需要大力手玩一下前三列的數字,使其每一行的數字和相等
對於剩下的 \(n/k-3\) 列的數字按 \(n/k\) 為偶數的情況做就好了
Code
#include <bits/stdc++.h>
#define re register
#define int long long
// #define lls long long
#define pir make_pair
#define fr first
#define sc second
#define db long double
using namespace std;
const int mol=998244353;
const int maxn=1e6+10;
const int INF=1e9+10;
inline int qpow(int a,int b) { int ans=1; while(b) { if(b&1) (ans*=a)%=mol; (a*=a)%=mol; b>>=1; } return ans; }
inline int read() {
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
int t,n,k,tot,fac[maxn];
vector<int>vec[maxn];
inline void wor() {
for(re int i=1;i<=tot;i++) vec[i].clear();
tot=n/k;
for(re int i=1;i<=tot;i++) for(re int j=1;j<=k;j++) vec[i].push_back(j);
if(tot&1) {
if(fac[k]*3%k!=0) { puts("No"); return ; }
int lim=fac[k]*3/k,fg=lim-1-k;
for(re int i=1;i<=k;i++) { vec[2][i-1]=fg; vec[3][i-1]=lim-i-fg; fg++; if(fg>k) fg=1; }
for(re int i=1;i<=(tot-3)/2;i++) reverse(vec[3+i].begin(),vec[3+i].end());
} else {
for(re int i=1;i<=tot/2;i++) reverse(vec[i].begin(),vec[i].end());
}
puts("Yes");
for(re int i=1;i<=k;i++) {
for(re int j=1;j<=tot;j++) printf("%lld ",(j-1)*k+vec[j][i-1]);
puts("");
}
}
signed main(void) {
freopen("erp.in","r",stdin); freopen("erp.out","w",stdout);
int lims=1e6;
for(re int i=1;i<=lims;i++) fac[i]=fac[i-1]+i;
t=read();
while(t--) {
n=read(); k=read();
if(n==1) { puts("Yes 1"); continue; }
if(n/k==1) { puts("No"); continue ; }
wor();
}
}