杭电2019多校第四场 HDU-6616 Divide the Stones (构造+思维)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=6616

题意:T组样例,每组样例给出n和k(保证k是n的因子)。有n堆石子,第i堆有i个石子。问能否把这n堆石子分成k组,并且每组的石子和相等。(不能把某一堆的石子拿到另一堆)能就是输出"yes",并输出方案,没有就输出"no"。

思路:显然,总共有 1+2+....+n=n*(n+1)/2=m个石子。如果m不是k的倍数,那么肯定是“no”。m是k的倍数也就是,n*(n+1)/2能整除k,也就是n*(n+1)/2/k是整数,又因为k是n的因子,那么式子变为(n/k)*(n+1)/2,那么既然能整除,要么n/k是偶数,要么(n+1)是偶数。

我们分类讨论:

(1)当n/k是偶数时,那么一定有解。我们可以把n堆石子分为n/2对,例如(1,n)、(2,n-1)、、、(3,n-2)。然后把这n/2对分为k组即可。举个例子,假设n=12,k=3。我们就可以把n堆石子分为(1,12)、(2,11)、(3,10)、(4,9)、(5、8)、(6、7)。然后第一组:(1,12)、(2、11);第二组:(3,10)、(4,9);第三组:(5,8)、(6,7)。推出公式为,假设x=n/k/2。第i组(i从0开始)就为:(i*x+1,n-(i*x+1)+1),(i*x+2,n-(i*x+2)+1)、、、(i*x+x,n-(i*x+x)+1)。

(2)当n/k是奇数时,当n为偶数时,n+1为奇数,上式m/k=n*(n+1)/2/k=(n/k)*(n+1)/2,n/k是奇数,n+1是奇数,不可能整除2,无解。n为奇数时,当n==k时,只有当n==1时有解,其余情况无解;否则(即n为奇数且n/k为奇数且n!=k),那么n/k>=3,我们不管前3k堆,那么(n-3k)/k/2=(n/k-3)/2,因为n/k>=3且n/k是奇数,那么n/k-3肯定是偶数。那么剩下的这n-3k堆,我们就可以按照上述n/k是偶数的方法分成k组,只不过是从3*k+1开始分。至于前3k堆,我们就要想办法把他们分为k组,也就是每组3堆,分成k组相等的。再举个例子,假设k=9,3k=27。我们可以把问题转化一下,把1、2、3、、、10、11、12、、、19、20、21、、、分成9 组,相当于把1、2、3、、、1+9、2+9、3+9、、、1+18、2+18、3+18、、、分成9组,也就是把1、2、3、、、1、2、3、、、1、2、3、、、,(即3组1、2、3、4、5、6、7、8、9)分为9组,每组3堆。唯一要求是每组的3堆中不能有相同的数字。

一种巧妙的分法为:

(1、2、3、4、5、6、7、8、9)+0

(5、6、7、8、9、1、2、3、4)+9

(9、7、5、3、1、8、6、4、2)+18

同一列的为1组。也就是每一组的第1堆都为i。前k/2组的第2堆为(k+1)/2+i+k,第3堆为k-(2i-1)+2k。后n-k/2组的第2堆为

i-k/2+k,第3堆为k-2(i-k/2-1)+2k。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,k,nn;
int main(void)
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&k);
		nn=n;
		if(n==1){
			printf("yes\n1\n");
			continue;
		}	
		if((n/k)&1)
		{
			if(k==1)
			{
				printf("yes\n");
				for(int i=1;i<=n;i++)
					printf("%d%c",i,i==n?'\n':' ');
				continue;
			}
			if(n==k||(n&1)==0) printf("no\n");
			else
 			{
 				printf("yes\n");
 				n-=3*k;
 				int x=n/k/2;
 				for(int i=1;i<=k;i++)
				{
					if(i<=k/2)
						printf("%d %d %d%c",i,(k+1)/2+i+k,k-(2*i-1)+k+k,n==0?'\n':' ');
					else
						printf("%d %d %d%c",i,i-k/2+k,k-2*(i-k/2-1)+k+k,n==0?'\n':' ');
					for(int j=1;j<=x;j++)
						printf("%d ",3*k+(i-1)*x+j);
					 for(int j=1;j<=x;j++)
					 	printf("%d%c",nn-((i-1)*x+j)+1,j==x?'\n':' ');
				}		
			}
		}
		else
		{
			printf("yes\n");
			int x=n/k/2;
			for(int i=0;i<k;i++)
			{
				for(int j=1;j<=x;j++)
					printf("%d ",x*i+j);
				for(int j=1;j<=x;j++)
					printf("%d%c",n-(x*i+j)+1,j==x?'\n':' ');
			}
		}
	}
	
	
	return 0;	
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值