莫比乌斯反演+容斥+分块优化
莫比乌斯反演学习资料:
POPOQQQ的莫比乌斯反演论文
那篇论文提及了莫比乌斯反演的第一种形式,另外一种形式可以看莫比乌斯反演定理证明(两种形式)
做这道题时,参考了【莫比乌斯反演】[HYSBZ/BZOJ2301]Problem b
这篇题解里,感觉莫比乌斯反演的推算部分,i和d弄错了,但是之后的分块优化以及代码是对的。可能博主没注意。
//#include<bits/stdc++.h>
#include<stdio.h>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
const int MAXN=50050;
int p[MAXN],mu[MAXN],sum[MAXN],f[MAXN];
int cal(int n,int m)//计算1<=x<=n&&1<=y<=m的情况下,(x,y)互质的对数
{
int limit=min(m,n),last,i,ret=0;
for(i=1;i<=limit;i=last+1)
{
last=min(n/(n/i),m/(m/i));//分块优化,查询(n/i)*(m/i)不变的情况下,i的区间[i,last]
ret+=(sum[last]-sum[i-1])*(n/i)*(m/i);//[i,last]区间内,n/i与m/i的值,没有变化,可以通过前缀和计算
}
return ret;
}
int main()
{
int i,j,ans,n,a,b,c,d,k,pcnt;
pcnt=0;
memset(p,0,sizeof(p));
memset(f,0,sizeof(f));
mu[1]=sum[1]=1;
for(i=2;i<MAXN;i++)//莫比乌斯反演模板
{
if(!f[i])//若为素数
{
p[++pcnt]=i;//记录下素数
mu[i]=-1;//u(i)为-1
}
for(j=1;p[j]*i<MAXN;j++)//对i的素数倍数,进行筛排
{
f[p[j]*i]=1;//标记该数为合数
if(i%p[j]==0)//若i*p[j]能被p[j]*p[j]整除
{
mu[p[j]*i]=0;//u(i)为0
break;//结束循环,之后的暂时没有必要继续计算
}
mu[p[j]*i]=-mu[i];//若p[j]*i包含不同的素数,则u(p[j]*i)=-u(i)
}
sum[i]=sum[i-1]+mu[i];//记录前缀和
}
scanf("%d",&n);
while(n--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
a--;c--;
a/=k;b/=k;c/=k;d/=k;
ans=cal(b,d)-cal(a,d)-cal(b,c)+cal(a,c);//容斥
printf("%d\n",ans);
}
}