链接: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;
}