HDU多校第三场 1003 Divide the Stones —— 构造

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n n 个数字 1 1 1 ~ n n n ,分为 k k k
    要求每组数字和相同
    若能分则输出分组的每个数

解题思路:

    讨论可以分的情况,设 m = n / k m = n / k m=n/k
    则问题转化为了将 1 1 1 n n n 填入 m m m k k k 列的矩阵,每一列的和相等
    若 m m m 为偶数,根据等差数列的相关性质,可以蛇形放入

1 1 1 2 2 2 3 3 3 4 4 4
8 8 8 7 7 7 6 6 6 5 5 5
9 9 9 10 10 10 11 11 11 12 12 12
16 16 16 15 15 15 14 14 14 13 13 13

    若 m m m 为奇数,则很明显不能满足上述要求
    但是可以先处理出前三行,剩下的按照偶数处理
    发现前三行可以按照如下方法构造:
    第一行从 1 1 1 k k k,第二行从中间开始往右递增,然后从第二行到中间再递增
    第三行根据第一二行构造即可

− 1 -1 1 1 1 1 − 2 -2 2 0 0 0 2 2 2平均值
1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 3 3 3
9 9 9 10 10 10 6 6 6 7 7 7 8 8 8 8 8 8
14 14 14 12 12 12 15 15 15 13 13 13 11 11 11 13 13 13

    如上所示,平均值代表那一行的平均值
    第一行代表前两行的和 与 前两行的和平均值的差值
    这样构造出来的差值不会重复,故可以用差值来构造第三行

核心:规律有点小奥妙

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
using pii = pair <ll,int>;
int T, n, k, m;

void gao1(){
	int tot = (1+3*k)*3/2;
	for(int i=1; i<=k; i++){
		for(int j=1; j<=m; j++){
			if(j == 1) printf("%d ", i);
			else if(j == 2) {
				if(i >= (k+1)/2) printf("%d ", i-(k+1)/2+k+1);
				else printf("%d ", i+k+(k+1)/2);
			}
			else if(j == 3){
				if(i >= (k+1)/2) printf("%d ", tot-i-(i-(k+1)/2+k+1));
				else printf("%d ", tot-i-(i+k+(k+1)/2));
			} 
			else{
				if(j & 1) printf("%d ", (j-1)*k+i);
				else printf("%d ", j*k-i+1);
			}
		}
		puts("");
	}
}

void gao2(){
	for(int i=1; i<=k; i++){
		for(int j=1; j<=m; j++){
			if(j & 1) printf("%d ", (j-1)*k+i);
			else printf("%d ", j*k-i+1);
		}
		puts("");
	}
}

int main() {
	scanf("%d", &T);
	while(T--){
		scanf("%d%d", &n, &k);
		m = n / k;
		if(1ll*(n+1)*n/2 % k) {
			puts("no");
			continue;
		}
		puts("yes");
		if(m & 1) gao1();
		else gao2();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值