HDU-4473 Exam 数学分析
题意:给定一个数x,现在定义f(x)是a*b|x的情况下,a,b的组合情况有多少种。现在给定一个数N求f(1)+f(2)+...+f(N)。
解法:一开始想到了a*b是x的约数,因此推出了f(x)的公式,也就是这个公式,使得我们在求前缀和的道路上寸步难行......
正确的解法是对于任意一个a*b|c可以视为a*b*c = x,那么就把前缀和问题转化为a*b*c <= x的解的组合个数问题了。
三个数a,b,c先暂时不考虑其位置关系带来的不同,规定a<=b<=c,那么分情况讨论:
1.当a=b=c时有一种排列情况
2.当a=b<c时有三种排列情况
3.当a<b=c时有三种排列情况
4.当a<b<c时有六种排列情况
现在就是要枚举出这四种情况,通过学习,知道了一个高精度开方根的写法,说是高精度其实也就是进行一次可能的修正而已。
由于a是最小的一个数,所以有,通过枚举a之后,那么问题就转化为:,接着就能够枚举第2,3,4中情况了。对于第一种情况可以直接开立方求出。
注意求解的过程中要保证a,b,c的非递减关系。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; inline int sqrt2(LL x) { // 高精度开平方 LL LIM = (int)pow(1.0*x, 1.0/2); while (LIM*LIM < x) ++LIM; while (LIM*LIM > x) --LIM; return LIM; } inline int sqrt3(LL x) { LL LIM = (int)pow(1.0*x, 1.0/3); while (LIM*LIM*LIM < x) ++LIM; while (LIM*LIM*LIM > x) --LIM; return LIM; } LL solve(LL N) { LL LIM = sqrt3(N), ans = LIM; // a=b=c的种数 for (int i = 1; i <= LIM; ++i) { // 至少有一个数小于 LL ni = N / i, k = sqrt2(ni); ans += (ni/i + k - i*2)*3; // a=b<c的种数为ni/i-i 减去i都是为了保持非递减 // a<b=c的种数为k-i for (int j = i+1; j <= k; ++j) { ans += (ni/j - j) * 6; // a<b<c的种数 } } return ans; } int main() { int ca = 0; LL N; while (scanf("%I64d", &N) != EOF) { printf("Case %d: %I64d\n", ++ca, solve(N)); } return 0; }