mayoko’s diary

プロコンとかいろいろ。

Educational Codeforces Round 5 E. Sum of Remainders

解法

前似たような問題解いてましたね…
mayokoex.hatenablog.com
上の記事のは問題設定がわかりにくいですが, 今回のは問題設定が率直でわかりやすいです。

10^7 程度までは正直に余りを計算します。

それ以上の数での余りは, n/div の商が r になるものが O(sqrt(n)) しか考えられないことを利用します。ある数 div について, n/div = n/DIV を満たす最大の DIV を二分探索で求めます。すると, あまりの合計は,
(n-div*r) + (n-(div+1)*r) + ... + (n-DIV*r) = num*n - (div+DIV)*num/2 (num = DIV-div+1) です。

const ll MOD = 1e9+7;

// extgcd
ll extgcd(ll a, ll b, ll& x, ll& y) {
    ll d = a;
    if (b != 0) {
        d = extgcd(b, a % b, y, x);
        y -= (a / b) * x;
    } else {
        x = 1; y = 0;
    }
    return d;
}
// mod_inverse
ll mod_inverse(ll a, ll m) {
    ll x, y;
    extgcd(a, m, x, y);
    return (m+x%m) % m;
}

ll mul(ll x, ll y) {
    if (y==0) return 0;
    if (y==1) return x;
    if (y%2) return (x*mul(x, y-1))%MOD;
    ll tmp = mul(x, y/2);
    return (tmp*tmp)%MOD;
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    ll n, m;
    cin >> n >> m;
    ll ans = 0, maxi = 0, inv2 = mod_inverse(2, MOD);
    for (ll i = 1; i <= min(m, 10000000ll); i++) {
        maxi = i;
        (ans += n%i) %= MOD;
    }
    if (maxi < m) {
        ll div = maxi+1;
        while (div < m) {
            ll r = n/div;
            ll low = div, high = m+1;
            while (high-low > 1) {
                ll med = (high+low)/2;
                if (n/med == r) low = med;
                else high = med;
            }
            ll num = low-div+1;
            ll tmp = (n%MOD)*(num%MOD)%MOD;
            ll minus = (r%MOD)*((div+low)%MOD);
            minus %= MOD;
            (minus *= (num%MOD)) %= MOD;
            (minus *= inv2) %= MOD;
            tmp -= minus;
            (ans += tmp) %= MOD;
            div = low+1;
        }
    }
    ans = (ans+MOD)%MOD;
    cout << ans << endl;
    return 0;
}