hdu6134-莫比乌斯反演+思维



题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6134


题意:

让你求


直接枚举求前缀和是o(n^2)的复杂度,肯定会超时,应该想办法优化的复杂度,

然后求前缀和,就可以得到要求的f(n)


,则


根据莫比乌斯反演,求得



计算时,直接枚举复杂度太高,可以枚举j和t,,这样时间复杂度为

n/1 + n/2 + n/3 + ... + n/n = n * (1 + 1/2 + 1/2 + ... + 1/n) ≈ n * log(n)


可以知道,每个t对区间有贡献,所以让g[(t - 1) * j + 1] += t, g[t * j] -= t, 

然后g求前缀和即得到所求的g。再处理出莫比乌斯函数就能够求f了。


时直接枚举i,d复杂度为o(n * sqrt(n)),会超时,

应该枚举d,然后枚举i = k * d,这样复杂度为n/1 + n/2 + n/3 + ... n/n = n * (1 + 1/2 + 1/3 + ... + 1/n) ≈ o(n * log(n)),

总的时间复杂度为o(n * log(n))



代码:

# include 
# include 
# include 
# include 
# include 
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 5;
int vis[maxn];
int pri[maxn];
int mu[maxn];
int g[maxn];
int f[maxn];
int n;
int len;

void mobius() {
    memset(vis, 0, sizeof vis);
    mu[1] = 1; len = 0;
    for (int i = 2; i < maxn; ++i) {
        if (!vis[i]) {
            pri[len++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < len && i * pri[j] < maxn; ++j) {
            vis[i * pri[j]] = 1;
            if (i % pri[j]) mu[i * pri[j]] = -mu[i];
            else {
                mu[i * pri[j]] = 0;
                break;
            }
        }
    }
}

void init() {
    memset(g, 0, sizeof g);
    for (int j = 1; j < maxn; ++j) {
        for (int t = 1; (t - 1) * j + 1 < maxn; ++t) {
            int l = max((t - 1) * j + 1, j), r = min(t * j + 1, maxn - 1);
            g[l] = ((ll)g[l] + t) % mod;
            g[r] = (((ll)g[r] - t) % mod + mod) % mod;
        }
    }

    for (int i = 1; i < maxn; ++i) {
        g[i] = ((ll)g[i] + g[i - 1]) % mod;
    }
    memset(f, 0, sizeof f);
    for (int d = 1; d < maxn; ++d) {
        for (int i = d; i < maxn; i += d) {
            f[i] = ((ll)f[i] + (ll)mu[d] * g[i / d] % mod) % mod;
        }
    }
    for (int i = 1; i < maxn; ++i) {
        f[i] = ((ll)f[i] + f[i - 1]) % mod;
    }
}

int main(void)
{
    mobius(); init();
    while (~scanf("%d", &n))
        printf("%d\n", f[n]);

    return 0;
}

/*

100
1000
500
666
30
10000
100000
500000

100
17744
1000
2473330
500
566051
666
1041802
30
1275
10000
317187476
100000
716969323
500000
224647420
123456
986099818
871496
888437745
800000
408863358
600000
873233747
->872733683
850000
177648068
*/

你可能感兴趣的:(数学)