题目描述
The following rules define a kind of integer tuple - the Legend Tuple:
- (1, k) is always a Legend Tuple, where k is an integer.
- if (n, k) is a Legend Tuple, (n + k, k) is also a Legend Tuple.
- if (n, k) is a Legend Tuple, (nk, k) is also a Legend Tuple.
We want to know the number of the Legend Tuples (n, k) where 1≤n≤N,1≤k≤K.
In order to avoid calculations of huge integers, report the answer modulo 109+7instead.
输入描述:
The input contains two integers N and K,1≤N,K≤1012
输出描述:
Output the answer modulo 109+7
示例1
输入
3 3
输出
8
示例2
输入
3 9
输出
14
题解:
思路:(n,k)是Legend Tuple的条件是,当且仅当满足下面三个要求中的某一个:
-
- n == 1
- n是k的倍数
- n-1是k的倍数
所以我们可以遍历k,对于每个k,用n/k计算出满足第二个条件的个数,用(n-1)/k求出满足第三个条件的个数,所以对于每个k,其实Legend Tuple的个数有 n/k + (n-1)/k
又因为n, k非常的大,所以可以想到使用数论分块来实现(n/i + (n-1)/i) (i属于(1,k));
关于数论分块:
那么如何求每一块的左右边界?
这里还要讲解一个知识点:给定正整数 i 和 n 满足 i <= n ,使得 n / i = n / x成立的最大 x = n / (n / i) (证明过程参考某乎)
所以当左边界为 i 时,右边界为 n / (n / i)
1 数论分块代码 2 ll ans=0; 3 for(ll l=1,r;l<=n;l=r+1) 4 { 5 r=n/(n/l); 6 ans+=(r-l+1)*(n/l); 7 }
所以类比,此题也得解,代码如下
1 //数论分块 2 #include<iostream> 3 using namespace std; 4 5 typedef long long ll; 6 const ll M = 1e9 + 7; 7 ll n, k; 8 9 void solve() { 10 ll ans = (n % M + k % M - 1) % M; //因为任何(1,n),(n,1)属于Legend Tuple,又因为(1,1)重复,固然减去1 11 12 //数论分块代码块 13 14 //n = n 15 for (ll left = 2, right; left <= k; left = right + 1) { 16 ll x = n / left; 17 if (x == 0) break; 18 right = n / x; 19 ans = (ans + (x%M * (min(right, k) - left + 1) % M) % M) % M; //min(right, k)的意义在于,求左边界为i的右边界,i最大为k,固需要进行一次筛选 20 } 21 22 //n = n-1 23 for (ll left = 2, right; left <= k; left = right + 1) { 24 ll x = (n-1) / left; 25 if (x == 0) break; 26 right = (n-1) / x; 27 ans = (ans + (x%M * (min(right, k) - left + 1) % M) % M) % M; 28 } 29 cout << ans << " "; 30 } 31 32 int main() { 33 cin >> n >> k; 34 solve(); 35 return 0; 36 }