您的位置:首页 > 其它

【莫比乌斯反演】BZOJ1101 [POI2007]zap

2015-06-24 00:41 323 查看

Description

  回答T组询问,有多少组gcd(x,y)=d,x<=a, y<=b。T, a, b<=4e5。

Solution

  显然对于gcd=d的,应该把a/d b/d,然后转为gcd=1计算

  计算用莫比乌斯反演相信大家都会

  关键是有T组询问n^2会T

  于是有这样一个优化可以做到每次sqrt(n)

  

  每一次是ret+=mu[i]*(n/i)*(m/i)

  可是除法向下取整所以会导致很多i的(n/i)*(m/i)一样

  具体来说,向下取整得到的结果一定是约数所以对于(n/i)最多2sqrt(n)种

  那么(n/i)*(m/i)放一起也就4sqrt(n)种

  这个序列一定是不上升的,所以考虑对所有的(n/i)*(m/i)视为一块相同的一起算

  那么肯定要记录下mu[i]的前缀和

  如何快速得到每一块的l和r?

  每一块的r肯定要么n%i==0要么m%i==0

  于是用pos=min(n/(n/i),m/(m/i)) 定位

  当然pos+1就是下一块的l了

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=5e4+5;

int flag[maxn],prime[maxn],cnt;
int mu[maxn],sum[maxn];

int getmu(){
mu[1]=1;
for(int i=2;i<maxn;i++){
if(!flag[i]){
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;i*prime[j]<maxn&&j<=cnt;j++){
flag[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<maxn;i++)
sum[i]=sum[i-1]+mu[i];
}

int cal(int n,int m){
int ret=0,pos;
if(n>m) swap(n,m);
for(int i=1;i<=n;i=pos+1){
pos=min(n/(n/i),m/(m/i));
ret+=(sum[pos]-sum[i-1])*(n/i)*(m/i);
}
return ret;
}

int main(){
int T,a,b,d;
scanf("%d",&T);
getmu();

while(T--){
scanf("%d%d%d",&a,&b,&d);
a/=d,b/=d;
printf("%d\n",cal(a,b));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: