题意:将 重量为1到 n的物品 划分为k堆,每堆的个数相同 ,且重量相同。
思路:将相邻的k个物品分别分给k堆,可以想象成一个 ( n / k ) * k 的矩阵。注意到如果行数为偶数则很容易构造出来。如果为奇数行我们考虑 先 构造3行,再构造偶数行。三行的构造方法也很好想,感觉上就是轮换着构造。具体的可行性可通过循环群进行证明。
#include <bits/stdc++.h>
using namespace std;
typedef int lint;
typedef long long LL;
const lint maxk = 100001;
vector<lint> ve[maxk];
int main(){
lint T,n,k;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
for( lint i = 1;i <= k;i++ )ve[i].clear();
if( (n%k) || ((LL)n*(n+1)/2)%k ){
puts("no");
continue;
}
lint m = n/k;
if( (LL)(1+k)*m % 2 ){
puts("no");
continue;
}
puts("yes");
if( m%2 == 0){
for( lint i = 1;i <= k;i++ ){
lint s = i;
bool first = true;
for( lint j = 1;j <= m;j++ ){
if(first && j > m/2){
first = false;
s += (k+1)+(j-1)*k*2-s-s;
}
ve[i].push_back(s);
s += k;
}
}
for( lint i = 1;i <= k;i++ ){
lint sum = 0;
for( lint j = 0;j < m;j++ ){
printf("%d ",ve[i][j]);
sum += ve[i][j];
}
//printf("%d\n",sum);
printf("\n");
}
}else{
if( k == 1 ){
for( lint i = 1;i <= n;i++ ){
printf("%d ",i);
}
printf("\n");
continue;
}
lint a = 1,b = k/2+1;
for( lint i = 1;i <= k;i++ ){
ve[i].push_back(a);
ve[i].push_back( b+k );
ve[i].push_back( (LL)(3*k+1)*3*k/(2*k) - a - b - k );
a++;b = b%k+1;
}
for( lint i = 1;i <= k;i++ ){
lint s = i+3*k;
bool first = true;
for( lint j = 4;j <= m;j++ ){
if(first && j > (4+m)/2){
first = false;
s += (k+1)+(j-1)*k*2-s-s;
}
ve[i].push_back(s);
s += k;
}
}
for( lint i = 1;i <= k;i++ ){
lint sum = 0;
for( lint j = 0;j < ve[i].size();j++ ){
printf("%d ",ve[i][j]);
sum += ve[i][j];
}
//printf("%d\n",sum);
printf("\n");
}
}
}
return 0;
}