# BZOJ 2301 [HAOI2011]Problem b（莫比乌斯反演）

①莫比乌斯函数：

$\mu(x) = 1, n=1$

$\mu(x) = (-1)^k, x = p_{1}*p_{2}*p_{3}...*p_{k}$

$\mu(x) = 0, else$

else表示质因子分解后有质因子出现一次以上

②莫比乌斯反演的两种写法：

$F(i) = \sum_{d|i} f(d)\Rightarrow f(i) = \sum_{d|i}\mu(\frac{i}{d}) * F(d)$

$F(i) = \sum_{i|d} f(d) \Rightarrow f(i) = \sum_{i|d}\mu(\frac{d}{i})*F(d)$

③这道题的思路：

$f(k) = \sum_{k|d}\mu(\frac{d}{k})*[\frac{n}{d}]*[\frac{m}{d}]$

④整除分块：

......

n/(n/i)可以得到这个块的右界

⑤回到这题，怎么在这题中实现？

$f(k) = \sum_{k|d}\mu(\frac{d}{k})*[\frac{n}{d}]*[\frac{m}{d}]$

(i,last)每个k的倍数乘积都是$[\frac{n}{i}]*[\frac{m}{i}]$,这段区间的莫比乌斯函数范围是(i/k,last/k)，我们莫比乌斯函数提前预处理前缀和

⑥后来，不过已经不重要了

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define df(x) ll x;scanf("%I64d",&x)
#define df2(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define mod 1000000007
#define duozu(T) int T;scanf("%d",&T);while (T--)

const int N = 5e4+5;

int miu[N];
bool vis[N];
int prime[N>>2];

void euler()///欧拉筛求莫比乌斯函数
{
int tot = 0;
miu[1] = 1;
for (int i=2;i<=N-5;i++){
if (!vis[i]){
prime[tot++] = i;
miu[i] = -1;
}
for (int j=0;j<tot && prime[j]*i<=N-5;j++){
vis[i*prime[j]] = true;
if (i%prime[j]==0){
miu[i*prime[j]] = 0;
break;
}
else {
miu[i*prime[j]] = -miu[i];
}
}
}
}

ll query(int n,int m)
{
ll ans = 0;
int last;
if (n>m) swap(n,m);
for (int i=1;i<=n;i=last+1){
last = min(n/(n/i),m/(m/i));
ans += 1LL*(n/i)*(m/i)*(miu[last]-miu[i-1]);
}
return ans;
}

ll query(int n,int m,int k)
{
ll ans = 0;
int last;
if (n>m) swap(n,m);
for (int i=1;i<=n;i=last+1){
last = min(n/(n/i),m/(m/i));
ans += 1LL*(n/i)*(m/i)*(miu[last/k]-miu[(i-1)/k]);
}
return ans;
}

int main()
{
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
euler();
for1(i,2,N-5) miu[i] += miu[i-1];

duozu(T){
int a,b,c,d,k;
scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
ll ans = 0;

ans = query(b/k,d/k) - query(b/k,(c-1)/k) - query((a-1)/k,d/k) + query((a-1)/k,(c-1)/k);//这样快

//ans = query(b,d,k) - query(b,c-1,k) - query(a-1,d,k) + query(a-1,c-1,k);//这样慢

printf("%lld\n",ans);
}

return 0;
}


©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客