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;    
}

 

posted @ 2013-04-24 23:01  沐阳  阅读(531)  评论(0编辑  收藏  举报