0
点赞
收藏
分享

微信扫一扫

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。

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

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

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

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


针对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;
}

 



举报

相关推荐

0 条评论