前言:内容包括:题目,代码实现,大致思路,代码解读
题目:
描述
牛牛以前在老师那里得到了一个正整数数对(x, y), 牛牛忘记他们具体是多少了。
但是牛牛记得老师告诉过他x和y均不大于n, 并且x除以y的余数大于等于k。
牛牛希望你能帮他计算一共有多少个可能的数对。
输入描述:
输入包括两个正整数n,k(1 <= n <= 10^5, 0 <= k <= n - 1)。
输出描述:
对于每个测试用例, 输出一个正整数表示可能的数对数量。
示例1
输入:
5 2
输出:
7
说明:
满足条件的数对有(2,3),(2,4),(2,5),(3,4),(3,5),(4,5),(5,3)
代码实现:
#include <stdio.h>
int main()
{
long n = 0;
long k = 0;
scanf("%ld%ld",&n,&k);
long y = 0;
long count = 0;
if(k == 0)
{
printf("%ld",n*n);
}
else
{
for(y=k+1;y<=n;y++)
{
count+=((n/y)*(y-k))+((n%y<k) ? 0:(n%y-k+1));
}
printf("%ld",count);
}
return 0;
}
大致思路:
1. 暴力破解(两个for循环遍历)不可行,采用更优解法
2. 更优解法:
预备了解:1<= x <= n 1<= y <= n x%y>=k
由于x%y的余数要>=k, 故而y最终确定的取值范围是k+1~n
why: 若y是k,则x%y的余数的的值最大是k-1
a. 将x划分为几个区间:
[1, 2, ..., y]
[y+1, y+2, ..., 2y]
[2y+1, 2y+2, ..., 3y]
...
[ty+1, ty+2, ..., n]
以下是x的所有取值:1~n
1 | 2 | …… | k | …… | y-1 | y |
y+1 | y+2 | …… | k | …… | 2y-1 | 2y |
2y+1 | 2y+2 | …… | k | …… | 3y-1 | 3y |
3y+1 | 3y+2 | …… | k | …… | 4y-1 | 4y |
ty+1 | ty+2 | …… | k | …… | n |
x%y的余数:
1 | 2 | …… | k | …… | y-1 | 0 |
1 | 2 | …… | k | …… | y-1 | 0 |
1 | 2 | …… | k | …… | y-1 | 0 |
1 | 2 | …… | k | …… | y-1 | 0 |
1 | 2 | …… | k | …… | n%y |
由表可知
完整区间(除却最后一个区间)符合题意 余数>=k的值的范围:
k~y-1
即y-1-k+1 = y-k
eg. 区间1~3的个数:3-1+1=3
故而:只要是完整区间,符合条件的个数是y-k
最后一个区间 符合题意 余数>=k的值的范围:
k~n%y
即n%y-k+1
故而:最后一个区间符合条件的个数是n%y-k+1
代码解读:
part 1
if(k == 0)
{
printf("%ld",n*n);
}
k为0,由于任意数对的取模结果都是>=0的,故共有n*n中可能
part 2
for(y=k+1;y<=n;y++)
{
count+=((n/y)*(y-k))+((n%y<k) ? 0:(n%y-k+1));
}
printf("%ld",count);
k不为0,y的取值范围是:k+1~n
count:统计符合条件的个数
n/y:表示完整区间的个数
y-k:表示一个完整区间内x%y的余数>=k的个数
n%y:表示最后一段区间的长度,要分情况讨论:
情况一:最后一段区间的长度<k
这段区间最大的数字%y后的余数是自己,(y的范围是k+1~n)小于k
不会出现余数>=k的,即符合条件的个数是0
情况二:最后一段区间的长度>k
这段区间上一定会有%y后的余数>=k符合条件的值
个数是n%y-k+1
本期完。