码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj4176

时间:2018-02-24 14:53:53      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:就是   int   play   二次   blog   +=   cal   个数   type   

莫比乌斯反演

根据约数和个数公式

$ans = \sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{x|i}\sum_{y|j}{[gcd(i, j)==1]}$

交换枚举顺序

$ans = \sum_{x=1}^{n}\sum_{y=1}^{n}{[\frac{n}{x}][\frac{n}{y}]*[gcd(x, y)==1]}$

$=\sum_{x=1}^{n}\sum_{y=1}^{n}{[\frac{n}{x}][\frac{n}{y}]\sum_{d|x,y}{\mu(d)}}$

再交换枚举顺序

$=\sum_{d=1}^{n}{\mu(d)\sum_{i=1}^{\frac{n}{d}}{\frac{n}{di}}\sum_{j=1}^{\frac{n}{d}}{\frac{n}{dj}}}$

设$f(n)=\sum_{i=1}^{n}{\frac{n}{i}}$

那么$=\sum_{d=1}^{n}{\mu(d)f(\frac{n}{d})^{2}}$

这就可以分块求了,$\mu$用杜教筛求,$f$用二次分块

反演就是不断交换求和顺序或者改变枚举变量

技术分享图片
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
const int N = 1e7 + 5, P = 1e9 + 7;
int n;
ll ans;
int p[N], mark[N], mu[N];
map<int, ll> sum_1;
void ini() {
    mu[1] = 1;
    for(int i = 2; i < N; ++i) {
        if(!mark[i]) {
            p[++p[0]] = i;
            mu[i] = -1;
        }
        for(int j = 1; j <= p[0] && i * p[j] < N; ++j) {
            mark[i * p[j]] = 1;
            if(i % p[j] == 0) {
                mu[i * p[j]] = 0;
                break;
            }
            mu[i * p[j]] = -mu[i];
        }
    }
    for(int i = 1; i < N; ++i) {
        mu[i] += mu[i - 1];
    }
}
ll dj_m(int n) {
    if(n < N) {
        return mu[n];
    }
    if(sum_1.find(n) != sum_1.end()) {
        return sum_1[n];
    }
    ll ret = 1;
    for(int i = 2, j = 0; i <= n; i = j + 1) {
        j = n / (n / i);
        ret = (ret - (ll)(j - i + 1) * dj_m(n / i) % P + P) % P;
    }
    return sum_1[n] = ret;
}
ll calc(int n) {
    ll ret = 0;
    for(int i = 1, j = 0; i <= n; i = j + 1) {
        j = n / (n / i);
        ret = (ret + (ll)(n / i) * (j - i + 1) % P) % P;
    }
    return ret;
}
int main() {
    ini();
    scanf("%d", &n);
    for(int i = 1, j = 0; i <= n; i = j + 1) {
        j = n / (n / i);
        ll a = ((dj_m(j) - dj_m(i - 1)) % P + P) % P, b = calc(n / i);
        ans = (ans + a * b % P * b % P) % P;
    }
    printf("%lld\n", ans);
    return 0;
}
View Code

 

bzoj4176

标签:就是   int   play   二次   blog   +=   cal   个数   type   

原文地址:https://www.cnblogs.com/19992147orz/p/8465159.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!