2020牛客暑期多校第七场 H - Dividing(整除分块)

传送门


这篇博客是我看到的关于这道题最详细的解释了。这道题比赛时队友找到了一种神奇的规律,大概是 i = 1 k ( n % i = = 0 ? 2 n / i : 2 n / i + 1 ) \sum_{i=1}^{k}(n\%i==0?2n/i:2n/i+1) ,然后也写过了,赛后发现本题的标答实际上是我们都没有想到的:

因为起点是 1 1 ,每次可以加上 k k 也可以乘上 k k 去传递,无论是加 k k 还是乘 k k ,最后都不改变 n n%k 的值,然后发现实际上需要求的是 i = 1 k j = 1 n ( j % i = = 0       j % i = = 1 ) \sum_{i=1}^k\sum_{j=1}^n(j\%i==0 ~~||~~j\%i==1)

考虑到整除分块解决的刚好是 i = 1 k n i \sum_{i=1}^k\frac{n}{i} ,那么我们就可以得出本题的求解通式:

i = 1 k n i + i = 2 k n 1 i + ( k 1 ) \sum_{i=1}^k\frac{n}{i}+\sum_{i=2}^k\frac{n-1}{i}+(k-1)

需要注意的是为什么第二个式子是从 2 2 开始:因为本题一开始的 n n 是从 1 1 开始的, k = 1 k=1 时在第一个式子已经计算过了 n [ 1 , n ] n∈[1,n] 的情况,避免重复计算。为什么最后要加上 k 1 k-1 ,是因为 n 1 n-1 ( n 1 ) % k (n-1)\%k 一定存在,因为 ( 1 , 1 ) (1,1) 已经被计算过了,因此只需加上 k 1 k-1

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define lowbit(x) (x&(-x))
#define mkp(x,y) make_pair(x,y)
#define mem(a,x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<double,double> pdd;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const double dinf=1e300;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;

ll cal(ll n,ll k){
    ll ans=0;
    for(ll l=2,r;l<=min(n,k);l=r+1){
        r=min(k,n/(n/l));
        ans=(ans+(r-l+1)%Mod*(n/l)%Mod)%Mod;
    }
    return ans;
}

int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    ll n,k;
    cin>>n>>k;
    ll ans=n%Mod;
    ans=(ans+cal(n,k))%Mod;
    ans=(ans+cal(n-1,k))%Mod;
    ans=(ans+k-1)%Mod;
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/107890774