bzoj1257: [CQOI2007]余数之和sum
1257: [CQOI2007]余数之和sum
Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 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; }