JZOJ·礼物【数论】

Description–

圣诞节这天,某商店准备了N个礼品盒,分别用整数1-N进行编号。其中,编号为1的盒子中有一个糖果,编号为2的盒子中有2个糖果,。。。编号为N的盒子中有N个糖果。这天一早,中山幼儿园的K个小朋友一起来到这间商店。作为当天的第一批顾客,这些小朋友可以从这N个礼品盒中选出两个拿走。小朋友们商量了一会儿后决定,他们拿走的糖果并不一定要多,但是一定要能够刚好平分给每个人。即拿走的两个盒子中的糖果总数一定要使K的倍数。现在他们想知道一共有多少种方案可供选择。


Input–

每行两个正整数N和K,其中1<=N<=109,1<=K<=109。一行N=K=0
表示输入结束,这一行不用处理。

Output–

对输入中除了N=K=0外的每一行,输出一行,这一行只有一个数,即其相对应的输入所得到的方案数。


Sample Input–

1 1
3 2
5 2
50 50
0 0

Sample Output–

0
1
4
24


说明–

20%的数据N<=100;
80%的数据K<=1000;
每个输入文件最多有200行输入数据。


解题思路–

100%: (zlt)
如果有x,y,a,b,x%k == a,y%k == b,a+b == k,则(x+y)%k==0。根据这个特性,我们可以列张表,然后利用乘法原理,一一配对。然而,可以直接被k整除的,要特殊判断;k为偶数时也要特判断。
再分情况讨论:
1、k为奇数且n%k没过k一半时
2、k为奇数且n%k过k一半时
3、k为偶数且n%k没过k一半时
4、k为偶数且n%k过k一半时

在这里插入图片描述


代码–

#include<iostream>
#include<cstdio>
using namespace std;
long long n,k,l,t,z,x;
int main()
{
	freopen("gift.in","r",stdin);
	freopen("gift.out","w",stdout);
	
	scanf("%lld%lld",&n,&k);
	while (n!=0 || k!=0)
	{
		l=n/k;//l:长度为n的序列可以被分成l份,每份长度为k
		t=n%k;//余数
		z=(k-1)/2;//每一份的一半
		x=l*(l-1)/2+l*l*z;         //处理非↓
		if ((k-1)%2) x+=l*(l-1)/2; //余数部分
		if (t>0)                          //
		{                                 //
			if (t>=z) x+=l*z;             //
			else x+=l*t;                  //
			t-=z;                         //处理余数部分
		}                                 //
		if (t>0 && (k-1)%2)               //
			x=x+(l+1)*l/2-l*(l-1)/2,t--;  //“+(l+1)*l/2”是因为中间有数,所以把“l*(l-1)/2”做些改动;而“-l*(l-1)/2”是因为实际上这一段是莫得匹配滴;(当然也可以x+=l)
		if (t>0) x+=(l+1)*t;              //
		printf("%lld\n",x);
		scanf("%lld%lld",&n,&k);
	}
	
	return 0;
}

不懂的可以去看lyf大佬

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值