题目
给定a,b,c,d,k,求:
思路
显然两段区间所产生的的答案可以用容斥原理“归一”处理。
将两段区间用容斥原理处理,那么仅需考虑:
同时除以k:
这时候大名鼎鼎的莫比乌斯反演出场了,运用狄利克雷卷积可以推导出反演结论:
带入反演结论得:
交换求和顺序得:
因为中有个d的倍数,中有个d的倍数,所以可以把后面两个大省掉:
对于和,可以使用整除分块处理,详参上一篇题解。
代码
#pragma GCC optimise(2)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll z=1e7+9,N=1e7+9;
ll mu[z],p[z],np,f[z],xb[z];
bool isp[z];
ll n,a,b,c,d,k,ans;
void getmu()
{
memset(isp,1,sizeof(isp));
isp[1]=0;
mu[1]=1;
for(ll i=2;i<=N;i++)
{
if(isp[i])
{
p[++np]=i;
mu[i]=-1;
}
for(ll j=1;j<=np&&i*p[j]<=N;j++)
{
ll x=i*p[j];
isp[x]=0;
if(i%p[j]!=0)mu[x]=-mu[i];
else
{
mu[x]=0;
break;
}
}
}
for(ll i=1;i<=np;i++)
for(ll j=1;j*p[i]<=N;j++)
f[j*p[i]]+=mu[j];//考虑每一个质数k,对于k的倍数kk,将其加上mu[kk/k]
for(ll i=1;i<=N;i++)
xb[i]=xb[i-1]+f[i];
}
ll cal(ll n,ll m)
{
ll res=0,rr=min(n,m);
for(ll l=1,r;l<=rr;l=r+1)
{
r=min(n/(n/l),m/(m/l));
res+=(xb[r]-xb[l-1])*(n/l)*(m/l);
}
return res;
}
int main()
{
scanf("%lld",&n);
getmu();
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&b,&d);
if(b>d)swap(b,d);
ans=cal(b,d);
printf("%lld\n",ans);
}
return 0;
}