luoguP3455 [POI2007]ZAP-Queries

题意

\(f(n)=\sum\limits_{i=1}^{a}\sum\limits_{j=1}^{b}[gcd(i,j)==n],F(n)=\sum\limits_{n|d}f(d)\)

发现\(F(n)=\frac{a}{n}*\frac{b}{n}\),可以理解为对\(a\)以内的所有\(k*n\)都和\(b\)以内的\(k*n\)配对了一次。

由莫比乌斯反演:
\(f(n)=\sum\limits_{n|d}\mu(\frac{d}{n})F(d)\)
\(f(n)=\sum\limits_{n|d}\mu(\frac{d}{n})\frac{a}{d}*\frac{b}{d}\)
\(t=\frac{d}{n}\)
\(f(n)=\sum\limits_{t=1}^{min(\frac{a}{d},\frac{b}{d})}\mu(t)\frac{a}{t*n}\frac{b}{t*n}\)

\(ans=f(d)\)

除法分块即可。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50010;
int T;
int mu[maxn],sum[maxn];
ll a,b,d;
bool vis[maxn];
vector<int>prime;
inline void shai(int n)
{
	vis[1]=1;mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])prime.push_back(i),mu[i]=-1;
		for(unsigned int j=0;j<prime.size()&&i*prime[j]<=n;j++)
		{
			vis[i*prime[j]]=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++)sum[i]=sum[i-1]+mu[i];
}
inline ll solve(ll a,ll b,ll d)
{
	ll n=min(a/d,b/d),res=0;
	for(int l=1,r;l<=n;l=r+1)
	{
		r=min(a/(a/l),b/(b/l));
		res+=(a/(l*d))*(b/(l*d))*(sum[r]-sum[l-1]);
	}
	return res;
}
int main()
{
	shai(50000);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld%lld%lld",&a,&b,&d);
		printf("%lld\n",solve(a,b,d));
	}
	return 0;
}
posted @ 2019-11-27 09:29  nofind  阅读(105)  评论(0编辑  收藏  举报