zoukankan      html  css  js  c++  java
  • 洛谷 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} floor*i)

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

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

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

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

    对于每个这样的区间,在乘一个(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} floor),那么显然当(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;
    }
    
  • 相关阅读:
    1069. Prufer Code 夜
    CROCMBTU 2012, Elimination Round (ACMICPC) D. Restoring Table 夜
    CROCMBTU 2012, Elimination Round (ACMICPC) H. Queries for Number of Palindromes 夜
    1145. Rope in the Labyrinth 夜
    1721. Two Sides of the Same Coin 夜
    1182. Team Them Up! 夜
    1162. Currency Exchange 夜
    1056. Computer Net 夜
    FOJ 2013 A short problem
    Codeforces 11.23
  • 原文地址:https://www.cnblogs.com/fushao2yyj/p/9575048.html
Copyright © 2011-2022 走看看