数学、根号分治、整除分块

缺乏注意力导致的,本文用 $f$ 代替题面中的 $\operatorname{rev}$ 函数。

首先注意到 $n<p$ 时 $f(n,p) = n$,所以 $k \leq 10^{18}$ 这个范围是假的,能够计算 $\sum\limits_{i=2}^{n} f(n,i)$ 即可完成全部做法。

另外注意到 $\sum\limits_{i=2}^{\sqrt{n}} f(n,i)$ 可以暴力计算。

然后我到此为止了,缺乏注意力导致的。

注意到 $\sqrt{n} < p$ 时 $n$ 的 $p$ 进制表示只有两位,分别是 $\left\lfloor \dfrac{n}{p} \right\rfloor$ 和 $n \bmod p$,前者在数值表示中要乘上一个 $p$,推式子

$$\sum_{i=\sqrt{n} + 1}^{n} f(n,i) = \sum_{i=\sqrt{n} + 1}^{n} \left( \left\lfloor \dfrac{n}{i} \right\rfloor + n \bmod i \times i \right)$$

公式化地把取模拆成乘除法组合。

$$= \sum_{i=\sqrt{n} + 1}^{n} \left( \left\lfloor \dfrac{n}{i} \right\rfloor + \left(n - \left\lfloor \dfrac{n}{i} \right\rfloor i \right) \times i \right)$$

$$= \sum_{i=\sqrt{n} + 1}^{n} \left( \left\lfloor \dfrac{n}{i} \right\rfloor + ni - \left\lfloor \dfrac{n}{i} \right\rfloor i \times i \right)$$

$$= \sum_{i=\sqrt{n} + 1}^{n} \left\lfloor \dfrac{n}{i} \right\rfloor + n \sum_{i=\sqrt{n} + 1}^{n} i - \sum_{i=\sqrt{n} + 1}^{n} \left\lfloor \dfrac{n}{i} \right\rfloor i^2$$

整除分块,瓶颈在 $\sum\limits_{i=2}^{\sqrt{n}} f(n,i)$ 的计算,总体时间复杂度 $O(\sqrt{n} \log n)$。

#include<bits/stdc++.h>
using namespace std;
constexpr int mod=1'000'000'007;
constexpr Mint<mod> I(1);
Mint<mod> ans;
int n,k,lim;
long long m;
Mint<mod> rev(int x,int y){
    static int w[30];
    Mint<mod> ret(0);
    int cnt=0;
    while(x) w[++cnt]=x%y,x/=y;
    for(int i=1;i<=cnt;i++) ret=ret*y+w[i];
    return ret;
}
inline Mint<mod> one(int l,int r){
    return I*(r-l+1);
}
inline Mint<mod> squ(int l,int r){
    Mint<mod> ret=I*r*(r+1)*(2*r+1)/6;
    ret-=I*(l-1)*l*(2*(l-1)+1)/6;
    return ret;
}
Mint<mod> calculate(int x,Mint<mod> (*sum)(int,int)){
    Mint<mod> ret(0);
    for(int l=1,r=1;l<=x;l=r+1){
        r=min(n/(n/l),x);
        ret+=sum(l,r)*(n/l);
    }
    return ret;
}
void solution(){
    cin>>n>>m,k=min(1ll*n,m);
    lim=sqrt(n);
    ans=max(m-n,0ll)%mod*n;
    for(int i=2;i<=min(lim,k);i++) ans+=rev(n,i);
    if(lim<k){
        ans+=calculate(k,one)-calculate(lim,one);
        ans+=I*n*((lim+1)+k)*(k-(lim+1)+1)/2;
        ans-=calculate(k,squ)-calculate(lim,squ);
    }
    cout<<ans<<'\n';
}
int T;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>T;
    while(T--) solution();
    return 0;
}
最后修改:2025 年 03 月 02 日
如果觉得我的文章对你有用,请随意赞赏