BZOJ1257(数论知识)
感觉做法很神奇……想不到啊qwq
题目:
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。
1<=n ,k<=10^9
Output
输出仅一行,即j(n, k)。
Sample Input
5 3
Sample Output
7
第一步,每个数x对ans的贡献为:k - k / x * x,所以ans = n * k - Σ(int)(k / x) * x;而怎样小于O(n)地求Σ里的一串呢?只好用数学知识压缩计算范围。
第二步,存在g(x) = (int)(k / (int)(k / x)),(如果没有int的取下界那gx就是x了,以下gx不写括号了啊),gx >= (int)(k / (k / x)) == (int)x == x,因此进一步有(int)(k / gx) <= (int)(k / x)(想一想为什么)。
第三步,(int)(k / gx) >= (int)(k / (k / (int)(k / x))) == (int)(k / x)。这个结论与第二步末尾结合一下可得,(int)(k / gx) == (int)(k / x)。这个推论就很重要了,意味着对于i∈[x, gx],(int)(k / i)都是一样的,而我们要求的Σ里面那一坨,不过就是(int)(k / i) * i,那[x, gx]就是等差数列了,可以O(1)算出这一区间的值,而不是遍历。
第四步,如此,遍历的将是区间[1, g(1))],[g(1)+1, g(g(1)+1)],……那这样的区间有多少个?复杂度可以承受吗?回答是,最多有2√k个区间。x <= √k时,最多只有√k个取值;x > √k时,考虑每个区间的“特征值”(int)(k / x) < √k,因此区间的个数还是小于√k。至于x > k时,都是k / x都是0了……
1 #include <cstdio> 2 #include <algorithm> 3 #define ll long long 4 #define R(x) scanf("%lld", &x) 5 #define W(x) printf("%lld\n", x) 6 7 int main() { 8 ll n, k; 9 R(n), R(k); 10 11 ll ans = n * k; 12 for (int i = 1, gx; i <= n; i = gx + 1) { 13 gx = k / i ? std::min(k / (k / i), n) : n; 14 ans -= (k / i) * (gx - i + 1) * (i + gx) / 2; 15 } 16 17 W(ans); 18 }