洛谷 2261 [CQOI2007]余数求和

题目戳这里

一句话题意

\(\sum_{i=1}^{n} (k ~~\texttt{mod} ~~i)\)

Solution

30分做法:
说实话并不知道怎么办。
60分做法:
很明显直接一遍o(n)枚举 i 就可以求出。
100分做法:
对于每一个k mod i,我们知道k mod i = k-k/i*i,那么
\(\quad \sum_{i=1}^{n}{k \quad mod \quad i}=n*k-\sum_{i=1}^{n}(k/i*i)\)
所以这个题目就转化成了求 \(\sum_{i=1}^{n}(k/i*i)\)
于是我们很敏锐地察觉到 k/i 貌似有点可操作性 (胡乱BB),因为我们发现对于一段区间的i,k/i 的值是一样的并且是连续的区间,例如:
100/25=100/24=100/23=100/22=100/21=4,因为这一段k/i的值都相等,那么可以一起计算,我们只需要运用等差数列求和公式求出21到25之和,再乘上4便是要求的值。
那么我们定义一个L和R,表示k/i的值相等的区间的两个端点。
首先我们可以知道L=上一次的R+1(区间都是连续的)。
因为L是当前区间中i的最小值,那么最大值就是k/(k/i)。
打个比方:21是100/i=4中最小的i,那么此区间中最大的就是100/4=25。
那么思路就很明显了:
首先Ans=n×k,再减去\(\sum_{i=1}^{n}(k/i*i)\)
对于公式\(\sum_{i=1}^{n}(k/i*i)\)
初始值L=1,R=0,然后L=上一个R+1,R=k/(k/L)。
每次Ans-=(k/L)×(R-L+1)×(L+R)/2(等差数列求和,应该都懂)。
直到R=n即可,注意R=min(k/(k/L),n),以防越界。
时间复杂度 巨说是\(o(\sqrt{n})\)
Ps:要开long long!!!!

Coding

60分代码

#include <bits/stdc++.h>
using namespace std;
_int main()
{
    long long n,k,ans=0;
    cin>>n>>k;
    for (long long i=1;i<=n;++i) 
        ans+=(k%i);
    cout<<ans;
    return 0;
}

100分代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long l,r,n,k,ans;
    cin>>n>>k;
    ans=n*k;
    l=1;
    r=0;
    while(r<n)
    {
        if(k/l!=0) r=min(k/(k/l),n);
        else r=n;
        ans-=(k/l)*(r-l+1)*(l+r)/2;
        l=r+1;
    }
    cout<<ans;
    return 0;
}

传说中的极简AC不易懂,233.

posted @ 2018-09-02 10:46  Le_Mon  阅读(145)  评论(0编辑  收藏  举报