P4449 于神之怒加强版[莫比乌斯反演]
P4449 于神之怒加强版[莫比乌斯反演]
题意: 给定i,j,k
求
solution:
一步一步推式子,
我们利用莫比乌斯反演,设
化简原式
由于
所以
于是我们想到换个思路,枚举
所以我们改变枚举顺序,将
于是我们只需枚举d便可以统计出所有答案,复杂度为
考虑
那么麻烦来了,我们如何快速计算出g函数
本题数据范围为
所以我们的解决办法就是线性筛
由于
考虑线性筛的过程
当
当
两种情况讨论 , 分类讨论的原因下面会讲
我们把
所以两者互质 , 即
换句话说 , 此时的
如果按照上面的步骤 , 我们会发现一个很尴尬的式子
下面上代码
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#pragma GCC optimize(2)
using namespace std;
inline int read() {
int res = 0 , flag = 1; char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') flag = -1; ch = getchar(); }
while(isdigit(ch)) { res = (res << 3) + (res << 1) + ch - '0'; ch = getchar(); }
return res * flag;
}
const int N=5e6;
const int mod=1000000007;
int mu[N+5],prime[N+5],ncnt;
ll k;
ll f[N+5],low[N+5];
ll g[N+5];
ll sum[N+5];
bool vis[N+5];
inline ll pow(ll a,ll b) {
ll ret = 1;
while(b) {
if(b & 1) (ret *= a) %= mod;
(a *= a) %= mod;
b >>= 1;
}
return ret;
}
inline void Mobius() {
mu[1] = 1;
g[1] = 1;//积性函数g[1] == 1!!!!!!!!!!!!!
for(register ll i = 1 ; i <= N ; ++ i) f[i] = pow(i,k);// , printf("%lld\n",f[i]);
for(register int i = 2 ; i <= N ; ++ i) {
if(!vis[i]) {
prime[++ncnt] = i;
low[i] = i;
mu[i] = -1;
g[i] = (f[i] - 1) % mod;
}
for(register int j = 1 ; j <= ncnt and i * prime[j] <= N ; ++ j) {
vis[i * prime[j]] = 1;//筛掉//每次由最小的累积
if(i % prime[j] == 0) {
// mu[i * prime[j]] = 0;
low[i * prime[j]] = low[i] * prime[j];
if(low[i] == i) {//i为素数的幂形式
g[i * prime[j]] = (g[i] * f[prime[j]]) % mod;// + f[1] * mu[i * prime[j]]
}
else {
g[i * prime[j]] = (g[i / low[i]] * g[low[i] * prime[j]]) % mod;
}
break;
}
else {
g[i * prime[j]] = (g[i] * g[prime[j]]) % mod;
low[i * prime[j]] = prime[j];
mu[i * prime[j]] = -mu[i];
}
}
}
for(register int i = 1 ; i <= N ; ++ i) sum[i] = (sum[i-1] + g[i]) % mod;
// for(register int i = 1 ; i <= N ; ++ i) printf("%lld\n",sum[i]);
}
inline int min(int a , int b) {
return a < b ? a : b;
}
inline ll work(int n,int m) {
ll ans = 0;
for(register int d = 1 , t; d <= min(n,m) ; d = t + 1) {
t = min(n / (n/d) , m / (m/d));
ans = (ans + 1ll * (n / d) * (m / d) % mod * ((sum[t] - sum[d - 1]) % mod + mod) % mod) % mod;
}
return ans;
}
int main() {
// freopen("god.in","r",stdin);
// freopen("god.out","w",stdout);
int test = read(); k = read();
Mobius();
while(test --) {
int n = read() , m = read();
printf("%lld\n",work(n,m));
}
return 0;
}
end
莫比乌斯反演套路总结
用莫比乌斯反演优化算法的题目一般都涉及到
优化的原理和关键是
而运用莫比乌斯反演的关键是将题目中的式子化成如下形式
然后就可以运用上述套路了
线性筛常见数论函数及自定义积性函数也是莫比乌斯反演可以考的一个板块
然而我还不会筛数论函数
以后有时间再写一篇这方面的博客