「HAOI2011」Problem b 题解

本文网址:https://www.cnblogs.com/zsc985246/p/17396656.html ,转载请注明出处。

前言

更好的阅读体验

莫比乌斯反演:对于两个数论函数 \(f(n),g(n)\),如果 \(f(n)=\sum_{d|n}g(d)\),那么 \(g(n)=\sum_{d|n}f(\frac{n}{d})\mu(d)\)

证明:

\(f(n)=\sum_{d|n}g(d)\) 代入,得到:

\[g(n)=\sum_{d|n}\mu(d)\sum_{x|\frac{n}{d}}g(x)\\ \]

\[\ \ \ \ \ \ \ =\sum_{x|n}g(x)\sum_{d|\frac{n}{x}}\mu(d)\\ \]

\[\ \ \ \ \ \ =\sum_{x|n}g(x)[\frac{n}{x}=1]\\ \]

\[=g(n)\ \ \ \ \ \ \ \ \ \ \ \ \ \]

得证。

注意,我们在它的证明中用到了莫比乌斯函数的一个性质:\(\sum_{d|n}\mu(d)=[n=1]\)

那么,我们可以反过来利用这个性质:\([n=1]=\sum_{d|n}\mu(d)\)

这便是莫比乌斯反演的本质。

题目大意

给出 \(a,b,c,d,k\),求:

\[\sum_{x=a}^{b}\sum_{y=c}^{d}[\gcd(x,y)=k] \]

思路

注意到,原式中有四个限制 \(a,b,c,d\)。尝试简化。

容易想到容斥。令 \(F(a,b)=\sum_{x=1}^{a}\sum_{y=1}^{b}[\gcd(x,y)=k]\),那么原式就等于 \(F(c,d)-F(a-1,d)-F(b-1,c)+F(a-1,b-1)\)

那我们只需要化简 \(F(a,b)\) 就好了。

\[F(a,b)=\sum_{x=1}^{a}\sum_{y=1}^{b}[\gcd(x,y)=k]\\ \]

\[\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =\sum_{x=1}^{a}\sum_{y=1}^{b}[\gcd(\frac{x}{k},\frac{y}{k})=1]\\ \]

\[\ \ \ \ \ \ \ \ \ \ \ \ =\sum_{x=1}^{\lfloor\frac{a}{k}\rfloor}\sum_{y=1}^{\lfloor\frac{b}{k}\rfloor}[\gcd(x,y)=1] \]

这里我们用 \([n=1]=\sum_{d|n}\mu(d)\)\([\gcd(x,y)=1]\) 拆开。

\[=\sum_{x=1}^{\lfloor\frac{a}{k}\rfloor}\sum_{y=1}^{\lfloor\frac{b}{k}\rfloor}\sum_{d|\gcd(x,y)}\mu(d)\\ \]

\[=\sum_{x=1}^{\lfloor\frac{a}{k}\rfloor}\sum_{d|x}\mu(d)\sum_{y=1}^{\lfloor\frac{b}{k}\rfloor}[d|y]\\ \]

\[\ \ \ \ \ \ =\sum_{d=1}^{\lfloor\frac{a}{k}\rfloor}\mu(d)\sum_{x=1}^{\lfloor\frac{a}{k}\rfloor}[d|x]\sum_{y=1}^{\lfloor\frac{b}{k}\rfloor}[d|y]\\ \]

\[=\sum_{d=1}^{\lfloor\frac{a}{k}\rfloor}\mu(d)\sum_{x=1}^{\lfloor\frac{a}{kd}\rfloor}\sum_{y=1}^{\lfloor\frac{b}{kd}\rfloor}1\ \ \ \ \ \\ \]

\[=\sum_{d=1}^{\lfloor\frac{a}{k}\rfloor}\mu(d)\lfloor\frac{a}{kd}\rfloor\lfloor\frac{b}{kd}\rfloor\ \ \\ \]

然后我们发现,可以直接使用整除分块计算答案了。

代码实现

两个数的整除分块不要写错了。

#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;

ll mu[N],tot,p[N],vis[N];
void init(ll n){//筛莫比乌斯函数
	mu[1]=1;
    For(i,2,n){
        if(!vis[i])p[++tot]=i,mu[i]=-1;
        For(j,1,tot){
            if(i*p[j]>n)break;
            vis[i*p[j]]=1;
            if(i%p[j]==0)break;
            mu[i*p[j]]=-mu[i];
        }
    }
    For(i,1,n)mu[i]+=mu[i-1];//前缀和
}

ll calc(ll a,ll b){
	ll ans=0,k;
	for(ll i=1;i<=min(a,b);i=k+1){//两个数的整除分块,不要写挂了
		k=min(a/(a/i),b/(b/i));
		ans+=(a/i)*(b/i)*(mu[k]-mu[i-1]);//按公式计算
	}
	return ans;
}

void mian(){
	
	ll a,b,c,d,k;
	scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
	a=(a-1)/k,b=b/k,c=(c-1)/k,d=d/k;//提前除以k
	printf("%lld\n",calc(b,d)-calc(b,c)-calc(a,d)+calc(a,c));//前缀和
	
}

int main(){
	init(1e6);
	int T=1;
	scanf("%d",&T);
	while(T--)mian();
	return 0;
}

尾声

如果你发现了问题,你可以直接回复这篇题解

如果你有更好的想法,也可以直接回复!

posted @ 2023-05-13 02:41  zsc985246  阅读(208)  评论(0)    收藏  举报