版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/89524062
题目描述
对于给出的 个询问,每次求有多少个数对 ,满足 , ,且 , 函数为 和 的最大公约数。
题解
先求 , 的满足 的数对:我们设 表示 的方案书, 表示 的方案数。
根据莫比乌斯反演公式,则一定有:
因此对于前者μ(d),我们可以使用前缀和来进行求解。对于后者,我们考虑除法分块来实现。
根据f数组的定义,我们可以知道 .
因此我们需要利用除法分块来求解 .显然对于 和对于 会形成不同的序列,每次对于i求解两个右端点中较小点 即可。
设 表示 的答案。
现在考虑原题,由于有范围限制,根据容斥原理就很容易就想到:
然后代码就很好实现了:
#include <bits/stdc++.h>
using namespace std;
const int N = 100000;
int Miu[N+10];
int vis[N+10];
int sum[N+10];
int prime[N+10];
void Find_Miu(void)
{
int m = 0;
Miu[1] = 1;
for (int i=2;i<=N;++i)
{
if (vis[i] == 0) prime[++m] = i, Miu[i] = -1;
for (int j=1;j<=m && i*prime[j]<=N;++j)
{
vis[i*prime[j]] = 1;
if (i%prime[j] == 0)
{
Miu[i*prime[j]] = 0;
break;
}
Miu[i*prime[j]] = -Miu[i];
}
}
return;
}
void Get_sum(void)
{
for (int i=1;i<=N;++i)
sum[i] = sum[i-1]+Miu[i];
return;
}
long long get(int a,int b,int d)
{
int j,n,m;
n = a/d;
m = b/d;
long long ans = 0;
for (int i=1;i<=min(n,m);i=j+1)//不要写成i++
{
j = min(n/(n/i),m/(m/i));
ans += (long long)(sum[j]-sum[i-1])*(n/i)*(m/i);
}
return ans;
}
void work(void)
{
int a,b,c,d,k;
scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
printf("%lld\n",get(b,d,k)-get(a-1,d,k)-get(b,c-1,k)+get(a-1,c-1,k));
return;
}
int main(void)
{
int n;
Find_Miu();
Get_sum();
scanf("%d",&n);
while (n -- ) work();
return 0;
}