礼物
jzoj 2129
题目大意
有1……n,n个礼物盒,第i个礼物盒有i个礼物,现在让你选2个礼物盒,使他是k的倍数
输入样例
1 1
3 2
5 2
50 50
0 0
输出样例
0
1
4
24
数据范围
20%的数据N<=100;
80%的数据K<=1000;
每个输入文件最多有200行输入数据。
解题思路:
对于20%的数据,我们可以直接暴力
对于80%的数据,我们可以按%k得到的值来分类,如果两个数%剩的值加在一起等于k那说明这两个数是合法的一对
正解:
首先我们图文结合(如下图)
首先我们把
n
n
n按
k
k
k来分(第2步)
然后我们把
n
/
k
n/k
n/k定为
x
x
x
每一个k中都有一个被整除的(第3步右侧),那x个被整除的,可以组成
(
x
−
1
)
∗
x
2
\frac{(x-1)*x}{2}
2(x−1)∗x对
然后剩下的
k
−
1
k-1
k−1我们分为奇偶数(第4步)
奇数:把中间除外的互相相乘,得到
h
∗
x
∗
x
h*x*x
h∗x∗x对(h为一半,每个余数都有x个数,两个就有
x
∗
x
x*x
x∗x对),然后剩下的一个(第5步)和其他k中的中间相乘,得到
(
x
−
1
)
∗
x
2
\frac{(x-1)*x}{2}
2(x−1)∗x对
偶数:直接相乘
然后处理余数:
不到一半或在一般前面的部分就直接和另一侧的x个数相配(第6步)
如果有中间1块就和前面
x
x
x个中间1块相配(第8步)
大于一半的就和一半前的
x
+
1
x+1
x+1相配(+1是因为第6步使一半前的加上了1)
注:解题思路写地不好请见谅
代码:
#include<cstdio>
#define min(a,b) (a)<(b)?(a):(b)
using namespace std;
long long n,k,x,ys,h,ans;
int main()
{
scanf("%lld %lld",&n,&k);
while (n&&k)
{
x=n/k;//如解题思路
ys=n%k;//余数
h=(k-1)/2;//减去整除部分后的一半
ans=x*(x-1)/2+x*x*h;//整除部分和普通部分
if ((k-1)%2) ans+=x*(x-1)/2;//奇数的中间部分
if (ys>0) ans+=(min(ys,h))*x,ys-=h;//余数的左边部分
if (ys>0&&(k-1)%2) ans+=x,ys--;//奇余数的中间部分
if (ys>0) ans+=ys*(x+1);//余数的右边部分
printf("%lld\n",ans);
scanf("%lld %lld",&n,&k);
}
return 0;
}