jzxx. 3130 我爱华杯赛
第22届华杯赛结束啦!还记得这道题吗?从自然数 1 至 2017 中选出一个数 n , 使得余下的 2016 个数的和除以 2003 的余 数与 n 除以 2003 的余数相同,则n=__________。今天把这题改编一下。(jzxx. 3130, code[vs]. 5848 )
把2017改为n,2003改为k,如果选择的数字t除以k的余数等于除t以外的其他数字的数字和除以k的余数相同则输出所有解t。如果此题无解,输出"Can't find the number!".
输入
一行,n和k。(n<=20000000;0<k<=n)(有1到n个数,k为除数)
输出
t的所有解,一行一个或“Can't find the number!”
样例输入
2017 2003
样例输出
1054
来源
http://oj.jzxx.net/problem.php?id=3130
【思路1】直接枚举i∈ [1~n],只要i%k==(sum-i)%k即出解。此法因是暴力枚举作取余运算,耗时较长,接近1秒的限时。
#include <cstdio>
int main()
{
long long n, i, k, t, sum, f = 1;
scanf("%lld%lld", &n, &k);
sum = n * (n + 1) / 2;
for (i = 1; i <= n; i++)
if (i % k == (sum - i) % k)
{
printf("%lld\n", i);
f = 0;
}
if (f)
printf("Can't find the number!");
return 0;
}
【思路2】数学解法,利用同余的性质。
先研究原题。列数学表达式如下:
(1+2+3+…+2017-n) mod 2003 = n mod 2003,所以,
(1+2+3+…+2017-n) ≡ n (mod 2003),即2017×2018/2-n与n同余。
由同余性质,两边同加n,得
2017×1009≡2n (mod 2003),注意,不能再同时除以2,因为同余不满足同除性。
因为2017×1009 mod 2003 = 105,所以2n要么是105,要么是2003+105,再大则超过2017*2的范围,因为选出的数n要在1~2017范围内。2n=105舍去,因为n必须是整数;于是2n=2003+105=2108,n=2108/2=1054。
根据上述推导,如果问题变为,从1~n中选出一个数t,使1~n的和减去t之后,就模k来说同余t。可以设计算法,先求出同余的这个余数r比如105之后,要验证是否能整除2,能整除则解t=r/2;接着再让r增加k,相当于进行下一个周期的情形,再判断是否能整除2……,如此循环,直到r值超过2n。以2n为限界,是因为t最大是n,那么2t就要在2n范围内。
本题有一个坑点,即r=0,比如1~10,其中取任何一个数后,剩余的数之和都与该数在模1上同余,因为任何数都能整除1。但是,解的范围已经限定为1~n不含0,所以出解时要特判不为0。注意,不能将特判r=0时的处理放到循环外,直接令r++,因为这会改变r的枚举取值。
#include <cstdio>
int main()
{
long long n, i, k, t, r, sum, f = 1;
scanf("%lld%lld", &n, &k);
sum = n * (n + 1) / 2; //类似2017*1009
r = sum % k; //类似105,同余的余数
for (i = r; i < n * 2; i += k) //类似105、105+2003、105+2003+2003…
if (i % 2 == 0 && i) //要特判i不为0
{
printf("%d\n", i / 2);//类似2108/2=1054
f = 0;//标记有解
}
if (f)
printf("Can't find the number!");
return 0;
}