HDU 5608 莫比乌斯反演 + 莫比乌斯函数前缀和

传送门: HDU 5608

附一下tls整理的求积性函数前缀和的姿势,
应该是杜教筛

author: skywalkert
original article: http://blog.csdn.net/skywalkert/article/details/50500009
last update time : 2017-04-01

题解:

令 G(n) = n ^ 2 - 3 * n + 2
先反演得:

f(n)=d|nG(d)μ(nd)

令:

A(n)=i=1nf(i)

则:

A(n)=i=1nf(i)=i=1nd|iG(d)μ(id)

然后从约数角度考虑对A(n)做出的贡献, 得

A(n)=i=1nd=1niG(i)μ(d)=i=1nG(i)Mni

其中:

M(n)=i=1nμ(i)

根据分块可以得到n / i在一段区间内是不变的
一段区间G(i)的和也可以用求和公式直接得到
那么现在就只要求M(n)

1=i=1n[i=1]=i=1nd|iμ(d)=i=1nd=1niμ(d)=i=1nM(ni)

因此:

M(n)=1i=2nM(ni)

同样用分块来做, 可以用线性筛预处理n^(2/3)以内的M(i)

code:


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1000001;
const int mo = 2333333;
const ll mod = 1e9 + 7;
bool isPrime[N];
int prime[N];
ll mobius[N];
ll ans;

inline ll getG(ll x)
{//求和公式
    ll mod1 = 6 * mod;
    ll res = x * (x + 1) % mod1 * (2*x + 1) % mod1 / 6;
    ll t = 3 * x * (x+1) % (2*mod) / 2;
    t = mod - t % mod;
    res = (res + t + x * 2) % mod;
    return res;
}

void init()
{   //预处理M(i)
    int cnt = 0;
    memset(isPrime, true, sizeof isPrime);
    isPrime[1] = false;
    mobius[1] = 1;
   // g[1] = 0;
    for(int i = 2; i < N; ++i)
    {
        if(isPrime[i])
        {
            prime[++cnt] = i;
            mobius[i] = -1;
        }
        for(int j = 1; j <= cnt && i * prime[j] < N; ++j)
        {
            isPrime[i * prime[j]] = false;
            if(i % prime[j] == 0)
            {
                mobius[i * prime[j]] = 0;
                break;
            }
            mobius[i * prime[j]] = -mobius[i];
        }
    }
    for(int i = 2; i < N; ++i)
    {
        mobius[i] = ((mobius[i] + mobius[i - 1]) % mod + mod) % mod;
      //  g[i] = getG((ll)i);
    }
}

ll n;

ll v[mo];
ll t[mo];
int last[mo], pre[mo];
int l;
void add(int x, ll y, ll z)
{
    t[++l] = y;
    pre[l] = last[x];
    last[x] = l;
    v[l] = z;
}

ll getSum(ll R, ll L)
{
    return ((getG(R) - getG(L)) % mod + mod) % mod;
}


ll cal(ll x)
{
    if(x < N)
    {
       return mobius[x];
    }
    int k = x % mo;
    for(int i = last[k]; i; i = pre[i])
        if(t[i] == x) return v[i];//hash做记忆化搜索
    ll r;
    ll ans = 1;
    for(ll i = 2; i <= x; i = r + 1)//分块
    {
        ll tmp = x / i;
        r = x / tmp;
        ll res = cal(tmp) * (r - i + 1) % mod;
        ans = ((ans - res) % mod + mod) % mod;
    }
    add(k, x, ans);
    return ans;
}
ll Calc(ll x)
{
    ll ans = 0;
    ll r;
    for(ll i = 1; i <= x; i = r + 1)
    {
        ll tmp = x / i;
        r = x / tmp;
        ll res = cal(tmp) * getSum(r, i - 1) % mod;
        ans = ((ans + res) % mod + mod) % mod;
    }
    return ans;
}
int main()
{
    init();
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n;
        ll res = Calc(n);
        cout << res << endl;
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值