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

xiaoxiao2021-02-28  87

传送门: 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; }
转载请注明原文地址: https://www.6miu.com/read-56029.html

最新回复(0)