2020牛客暑期多校训练营(第七场)H.Dividing(数论:整除分块)(详细)

地址:https://ac.nowcoder.com/acm/contest/5672/H?&headNav=acm

题意:

(1,k)是传奇元组

如果(n,k)是,那么(n+k,k)是

如果(n,k)是,那么(n*k,k)是

解析:

假设有传奇元组:(n*k,k)

那么(n*k+k,k)也为传奇元组,那么(n*k+k)%k==1

所以经过一系列尝试,发现满足传奇元组有两个条件:n%k==0||n%k==1

即:n%k==0  ||  (n-1)%k==0

可以想到,对n,k进行n*k的枚举,很显然,会T。

那么可以尝试固定一个n,枚举k。

可以发现,将n定为N,那么对于每一个k来讲,一列含有n/k个数满足n%k==0。

所以,固定N,枚举k即可。有公式:

(图来自https://www.cnblogs.com/charles1999/p/13424380.html)

针对n,k极大的情况,用到了除法分块

对于i<=k&&i<=n这里,需要说明一下,当n<k时,n/(n/i)这里出现了分母为0的情况,所以对于n<k,算到i==n即可。n>k,k算到底即可。

#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<string.h>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+20;
int a[maxn];
ll n,k;
ll ac(ll n)
{
    ll sum=0;
    ll i=2,j;
    for(;i<=n&&i<=k;i=j+1)
    {
        j=min((n/(n/i)),k);  //范围限定
        sum+=((j-i+1)%mod*(n/i)%mod)%mod;  //[i,j]范围的n/i相等,所以为j-i+1个n%i==0
    }
    return sum;
}
int main()
{

    cin>>n>>k;
    ll sum=(n+k-1)%mod;//最左边一列,最上面一行
    cout<<(sum+ac(n)+ac(n-1))%mod<<endl; 
}

 

posted @ 2020-08-04 00:16  liyexin  阅读(166)  评论(0编辑  收藏  举报