【luogu P1829】Crash的数字表格 (数论分块,莫比乌斯反演)
题意
求解式子
其中
分析
因为是单次询问,所需复杂度不高于O(n)
推导的思路:
将lcm转化成gcd
求和中有gcd, 转变求和顺序,先以相同的gcd分组,枚举gcd。
由gcd的性质可以知道
观察发现
换元
令
这里是在求解互质的数对的乘积和
则
考虑如何求解
因为
因为
所以用
于是这里满足的关系是
和刚刚一样的思路,转化为先枚举因子
换元
令
这里可以用公式计算出来
而前半部分的
则
可以分块求解
再次分块求解
求得最后答案
代码
#include<bits/stdc++.h>
using namespace std;
const int mod = 20101009;
typedef long long ll;
bool not_prime[10000001];
int mu[10000001];
int prime[1000001];
void mus(int n){
mu[1] = 1;int tot=0;
for(int i=2;i<=n;i++) {
if(not_prime[i]==0) prime[++tot] = i,mu[i]=-1;
for(int j=1;j<=tot&& prime[j]*i<=n;j++) {
not_prime[prime[j]*i] = 1;
if(i%prime[j]==0) {
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]] = -mu[i];
}
}
for(int i=1;i<=n;i++) {
mu[i] = mu[i] *1ll* i % mod * i % mod;
mu[i] += mu[i-1];
mu[i] %= mod;
}
}
int sum2(int n,int m){
int a = (n*1ll*(n+1)/2)%mod;
int b = (m*1ll*(m+1)/2)%mod;
return a*1ll*b%mod;
}
int sum(int n,int m) {
if(n>m) swap(n,m);
int res = 0;
for(int d=1;d<=n;) {
int en = min(n/(n/d),m/(m/d));
int a = sum2(n/d,m/d);
int b = (mu[en] - mu[d-1] + mod)%mod;
res += (a*1ll*b%mod);
res %=mod;
d = en+1;
}
return res;
}
int solve(int n,int m) {
if(n>m) swap(n,m);
int res = 0;
for(int d=1;d<=n;) {
int en = min(n/(n/d),m/(m/d));
int a = sum(n/d,m/d);
int b = ((d+en)*1ll*(en-d+1)/2)%mod;
res += (a*1ll*b%mod);
res %= mod;
d = en+1;
}
return res;
}
int main() {
int n,m;
cin>>n>>m;
mus(n);
cout<<solve(n,m)<<endl;
return 0;
int res =0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
int g = __gcd(i,j);
res += ((i*1ll*j / g) %mod);
res %=mod;
}
}
cout<<res<<endl;
}