luogu3935 Calculating

标题也许叫整除分块吧

\(1\)\(n\)因数的个数\(\sum_{i=1}^n(\sum_{d|n}1)\)

范围\(1e14\)时限3s

\(n\sqrt{n}\)的暴力铁定gg

分开考虑

\(1\)\(n\)中含有\(1\)因数的个数有\(n/1\)

含有2因数的个数有\(n/2\)**

······

含有n因数的个数有\(n/n\)

问题就转化为求\(\sum_{i=1}^{n}[\frac{n}{i}]\)

然后我们就可以把\(O(n\sqrt{n})\)的暴力转化为\(O(n)\)

可还是过不了&1e14的数据&

我们发现,我们求得\(\frac{n}{i}\)在一段区间内是连续的

而且呈现单调递减,这样我们就可以开心的套用二分啦

那到底有多少段连续的区间

把i分开考虑

1到\(\sqrt{n}\)之内,if都不同撑死有\(\sqrt{n}\)

\(\sqrt{n}\)到n之内,求\(\frac{n}{i}\)连续的一段,取值范围为1到\(\sqrt{n}\)之内,撑死也有\(\sqrt{n}\)

区间个数是\(\sqrt{n}\)级别的,二分是\(log\)级别的

所以复杂度为\(O(\sqrt{n}logn)\)

**一直以为这是根号的%>_<%**

参见牛客练习赛25(1e9)

#include <bits/stdc++.h>
using namespace std;
long long ans;
int l,n;
int main() {
    int q;
    cin>>q;
    while(q--) {
        cin>>n;
        l=1;
        ans=0;
        for(int i=1; i<=n; ++i) {
            int r=n;
            int mid=(l+r)>>1;
            while(n/l!=n/r) {
                mid=(l+r)>>1;
                r=mid;
            }
            ans+=n/l*(r-l+1);
            if(r==n) break;
            l=r+1;
        }
        cout<<ans<<"\n";
    }
    return 0;
}

直到我遇到了这个题luogu3935以及评测80sTLE的惨痛

才发现我是个zz诶

\(i\) \(1\) \(2\) \(3\) \(4\) \(5\) \(6\) \(7\) \(8\) \(9\) \(10\) \(11\) \(12\)
\(n/i\) \(12\) \(6\) \(4\) \(3\) \(2\) \(2\) \(1\) \(1\) \(1\) \(1\) \(1\) \(1\)

当我们知道\(l\)的时候,也就是一段的开头,如何快速找到我们要的r呢
\(n/l\)\(n\)中含有\(t=n/l\)块完整的\(l\)
那么\(n/t\)便是有\(t\)块最大的数,便是我们要求的\(r\)
所以\(r=n/(n/l)\)
所以我们求块的时间由二分的\(O(logn)\)变为了\(O(1)\)
复杂度为\(O(\sqrt{n})\)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
ll solve(ll n)
{
    ll ans=0;
    for(ll l=1,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        ans+=(r-l+1)%mod*(n/l)%mod;
        ans%=mod;
    }
    return ans;
}
int main() 
{
    ll x,y;
    cin>>x>>y;
    cout<<((solve(y)-solve(x-1))%mod+mod)%mod;
    return 0;
}

http://www.cnblogs.com/1000Suns/p/9193713.html

全部评论

相关推荐

头像
11-18 16:08
福州大学 Java
影流之主:干10年不被裁,我就能拿别人一年的钱了,日子有盼头了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务