2020暑期牛客多校训练营第七场(H)Dividing(整数分块)

Dividing

原题请看这里

题目描述:

以下规则定义了一种整数元组-传奇元组:

  • ( 1 k ) (1,k) 始终是传奇元组,其中k是整数。
  • 如果 ( n k ) (n,k) 是传奇元组,则 ( n + k k ) (n + k,k) 也是传奇元组。
  • 如果 ( n k ) (n,k) 是传奇元组, ( n k k ) (nk,k) 也是传奇元组。

我们想知道传奇元组 ( n k ) (n,k) 的数量,其中 1 n N 1 k K 1 \le n \le N,1 \le k \le K
为了避免计算巨大的整数,请输出答案取余 1 0 9 + 7 10^9+7 的值。

输入描述:

输入包含两个整数 N N K K 1 N K 1 0 12 1 \le N,K \le 10 ^ {12}

输出描述:

输出答案取模 1 0 9 + 7 10 ^ 9 + 7

样例:

样例输入1:

3 3

样例输出1:

8

样例输入2:

3 9

样例输出2:

14

思路:

我们观察题目意思可以发现,只有当 n = 1 n=1 或n是 k k 的倍数或 n 1 n-1 k k 的倍数时, ( n k ) (n,k) 是传奇元组。
所以,我们只要对于每一个确定的传奇元组 ( n , k ) (n,k) ,当且仅当 n n 可以减 k k / k /k ,最后变成1,于是我们得到了以下两种操作:

  • 如果 n n k k 的倍数,即 n = x k n=xk ,那么就可以减掉 ( x 1 ) (x-1) k k ,将 n n 变为 k k ,再 / k /k 变为 1 1 .
  • 如果 n 1 n-1 k k 的倍数, n = x k + 1 n=xk+1 ,那么就减掉 x x k k

因为 x x k k 中总有一个数小于 1 e 6 1e6 ,所以另一个数可以直接通过计算获取。

A C AC C o d e Code :

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll n,k,ans;
void f(ll n){
    for(ll i=2,j;i<=n&&i<=k;i=j+1){
        j=min(n/(n/i),k);
        ans=(ans+(j-i+1)%mod*(n/i)%mod)%mod;
    }
}int main(){
    scanf("%lld%lld",&n,&k);
    f(n);
    f(n-1);
    printf("%lld\n",(ans+n+k-1)%mod);
}

猜你喜欢

转载自blog.csdn.net/s260127ljy/article/details/107750277