题目:
牛牛以前在老师那里得到了一个正整数数对(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)
解题思路:
当我们确定了除数y的时候,我们会发现,对于x取任意的1-n中的数字,都会得到以下的余数数列:
1 2 3 4 5 ... (y-1) 0 1 2 3 4 5 ... (y-1) 0 ...
可以看到,余数的出现是存在循环的,每y个数余数就完成一次循环。当然最后一个循环可能只会有几个数,构不成一个循环,所以要分开来算。
算法中有三个变量需要我们计算:
- 循环次数
- 满循环时符合条件的余数数量
- 最后一个循环中符合条件的余数数量
有了这三个变量之后,我们就可以开始写对于y = i的情况,所有符合条件的x的数量。
res += (loopNum * (otherLoop > 0 ? otherLoop : 0) + (lastLoop > 0 ? lastLoop : 0));
最后把i为k-n的所有情况加总就可以得到正确解。
时间复杂度:O(N)
空间复杂度:O(1)
代码:
import java.util.Scanner; public class NumberPair { public static void main(String[] args) { NumberPair np = new NumberPair(); Scanner scanner = new Scanner(System.in); String cmd = scanner.nextLine(); String[] cmdParams = cmd.split(" "); int n = Integer.parseInt(cmdParams[0]); int k = Integer.parseInt(cmdParams[1]); System.out.println(np.getNumberPair(n, k)); } public long getNumberPair(int n, int k) { if (k == 0) return (long) n * (long) n; long res = 0; for (int i = k; i <= n; i ++) { /* n / i 表示余数的循环 i - k + 1 表示合法的余数的数量 n - n / i * i + 1最后一个余数循环里面最后剩下的几个余数 */ int lastLoop = (n - n / i * i - k + 1); int loopNum = (n / i); int otherLoop = (i - k); // System.out.println(i + " -> " + loopNum + " * " + otherLoop + " + " + lastLoop); res += (loopNum * (otherLoop > 0 ? otherLoop : 0) + (lastLoop > 0 ? lastLoop : 0)); } return res; } }