首页 > 技术文章 > bzoj1257: [CQOI2007]余数之和sum

zbtrs 2016-08-17 21:57 原文

1257: [CQOI2007]余数之和sum

Time Limit: 5 Sec  Memory Limit: 162 MB
Submit: 3453  Solved: 1597
[Submit][Status][Discuss]

Description

给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数。例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7

Input

输入仅一行,包含两个整数n, k。

Output

输出仅一行,即j(n, k)。

Sample Input

5 3

Sample Output

7

HINT

 

50%的数据满足:1<=n, k<=1000 100%的数据满足:1<=n ,k<=10^9

分析:一眼暴力题,然后发现数据好大啊......然后反应过来这是一道数学题,而题目本身就是一个未化简的公式,由于c++除法的向下取整性,可以得到k % i == k - (k / i) * i,所以我们化简.Σk % i (1 <= i <= n)     --->  Σk - (k / i) * i (1 <= i <= n)  ---> nk - Σi * (k / i).似乎比直接算还麻烦......不然也没办法了,观察这个式子,nk似乎没法化简了,只能把希望寄托在Σ,发现当k不变时,存在一段连续的i使k/i为定值,例如k = 5,i =3,4,5,k/i = 1,然后可以利用这一段连续区间来加快枚举速度,3,4,5可以想到什么?等差数列,套用小学学过的奥数公式,很容易求解,还要注意一点,n,k都小于等于10^9,那么很有可能爆int,所以用long long,还要注意怎么求区间下界,当枚举到一个新的k/i时,可以用公式k/k/i来求下界,也可以用二分来求,不过二分的时候mid = (l + r) / 2 + 1,一定要+1,否则会造成无限循环!

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

long long n, k, ans,chu;

long long qiuxiajie(long long l, long long r)
{
    while (l < r)
    {
        long long mid = (l + r) / 2 + 1;
            if (k / mid == chu)
            l = mid;
        else
            r = mid - 1;
    }
    return l;
}

int main()
{
    scanf("%lld%lld", &n, &k);
    ans = n * k;
    if (n > k)
        n = k;
    long long last = 0;
    for (int i = 1; i <= n; i = last + 1)
    {
        chu = k / i;
        last = qiuxiajie(i, n);
        ans -= (k / i) *(last + i)*(last - i + 1) / 2;
    }
    printf("%lld\n", ans);

    return 0;
}

 

推荐阅读