洛谷 P2261 [CQOI2007]余数求和

洛谷

一看就知道是一个数学题。嘿嘿~

讲讲各种分的做法吧。

30分做法:不知道,这大概是这题的难点吧!

60分做法:

一是直接暴力,看下代码吧~

#include <bits/stdc++.h>
using namespace std;
typedef int _int;
#define int long long

_int main()
{
    int n,k,ans=0;
    cin>>n>>k;
    for (int i=1;i<=n;++i) {
        ans+=(k%i);
    }
    cout<<ans;
    return 0;
}

第二种做法非常接近正解。

首先显然\(k~mod~i=k-\lfloor \frac{k}{i} \rfloor*i\)

所以我们马上一波转化,\(\sum_{i=1}^{n}k~mod~i=n*k-\sum_{i=1}^{n}\lfloor \frac{k}{i} \rfloor*i\)

那么这一截\(\sum_{i=1}^{n}\lfloor \frac{k}{i} \rfloor*i\)怎么求呢?

这个时候,直觉会告诉我们,\(\lfloor \frac{k}{i} \rfloor*i\)很有问题。

因为是向下取整,所以会有许多\(\lfloor \frac{k}{i} \rfloor\)是一样的。于是就会有一个一个的区间。

对于每个这样的区间,在乘一个\(i\)后,显然是一个等差数列。

不信看这个:

\((int)8/3=(int)8/4=2~~~~=>~~~~8/3*3+2=8/4*4\)

所以我们可以枚举\(i\),对于每一个\(i\),求出\(t=k/i\)

\(l=i,r=min(n,k)\)二分,如果\(mid/i=t,l\)扩大,否则\(r\)缩小。

找到后直接等差数列求和。

最后使\(i=r+1\)。这样表面时间复杂度是\(O(\sqrt{n}*log(n))\)

实则不然,因为我们的\(i\)跳跃的距离基本上很小很小,所以这代码比\(O(n)\)还慢!

看下代码吧!

#include <bits/stdc++.h>
using namespace std;
typedef int _int;
#define int long long

int n,k,ans;

_int main()
{
    cin>>n>>k;
    ans=n*k;
    for (int i=1;i<=min(n,k);++i) {
        int l=i,r=min(n,k),t=k/i,j=i;
        while (l<=r) {
            int mid=(l+r)/2;
            if (mid/i==t) l=mid+1,j=mid;
            else r=mid-1;
        }
        int a1=t*i,an=a1+(j-i)*t,g=j-i+1;
        ans-=(g*(a1+an)/2);
        i=j;
    }
    cout<<ans;
    return 0;
}

100正解:

有了上面第二个60分做法的思路,正解就不言而喻了。

只要把\(log(n)\)找区间改成\(O(1)\)就好了。

具体怎么改呢?

我们同样的枚举\(i\),假设区间为\([l,r]\),那么\(l=i\)显然,然后就剩\(r\)有点难搞了。

想想,我们每一段的公差都是\(\lfloor \frac{k}{i} \rfloor\),那么显然当\(k~mod~i=0\)时,\(r\)截止。

所以,\(r=k/(k/l)\)

那么,就完结了,上代码!真正的极简AC难懂~

#include <bits/stdc++.h>
using namespace std;
typedef int _int;
#define int long long
int n,k,ans;
_int main()
{
    cin>>n>>k;
    ans=n*k;
    for (int i=1;i<n;++i) {
        int l=i,t=k/l,r=t?min(n,k/t):n;
        int a1=t*l,an=a1+(r-l)*t,g=r-l+1;
        ans-=(g*(a1+an)/2);
        i=r;
    }
    cout<<ans;
    return 0;
}
posted @ 2018-09-02 19:55  fuyan0101  阅读(140)  评论(0编辑  收藏  举报