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

Dividing

原题请看这里

题目描述:

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

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

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

输入描述:

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

输出描述:

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

样例:

样例输入1:

3 3

样例输出1:

8

样例输入2:

3 9

样例输出2:

14

思路:

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

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

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

A C AC AC C o d e Code Code:

#include
#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);
}

你可能感兴趣的:(2020暑期牛客多校训练营第七场(H)Dividing(整数分块))