【HAOI2011】Problem b
学高考一段时间后我的OI实力居然越来越强了
原题:
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
(我的推导似乎和大部分人的不太一样
这个是距离高考还有10天写的……
因为快高考了所以放飞自我,自习课不想复习拿反演玩一玩,发现这个挺简单的啊,以前怎么学不会呢
大概是学高考增强了我的推公式的能力吧
啊不扯了,直接开始题解
首先必须要想到的思路是四项容斥简化问题
问题转化为求有对少个数对
看过其他反演题的话可能容易想到,
那么可以把n和m都除等于k,问题就转化为求
因为考虑到对于原来的满足条件的一个数对
显然若
现在的问题已经相对简单,剩下的就是推推推了~
这里应用到了
的性质
因为
先枚举所有可能的gcd,再枚举所有因子有这个gcd的i和j,不难想象和先枚举ij再枚举d所枚举到的东西是一样的,这个似乎是莫比乌斯反演里常用的转化方法
因为d和ij的枚举情况无关所以可以提到前面来
后面的东西就可以秒算了
至此公式推导完毕
我的推导和popoqqq的不太一样,他是构造了f和F并使用反演的公式,我是根据反演的性质来转化问题,现在看来两种方法本质是一样的
我的思路受《crash的表格》这道题的影响比较深
直接
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int rd(){int z=0,mk=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();}
return z*mk;
}
int n,m,n1,n2,m1,m2;
int mu[51000],mus[51000]; //mu's!!!!!!
int prm[51000],prt=0; bool prg[51000];
void gtmu(){
memset(prg,0,sizeof(prg));
mu[1]=mus[1]=1;
for(int i=2;i<=50000;++i){
if(!prg[i]) prm[++prt]=i,mu[i]=-1;
for(int j=1;prm[j]*i<=50000 && j<=prt;++j){
prg[prm[j]*i]=true;
if(!(i%prm[j])){ mu[i*prm[j]]=0; break;}
mu[prm[j]*i]=-mu[i];
}
mus[i]=mus[i-1]+mu[i];
}
}
int sm(int x,int y){ return (x+1)*x*(y+1)*y/4;}
int cclt(int x,int y){
if(x>y) swap(x,y);
int tmp,bwl=0;
for(int i=1;i<=x;i=tmp+1){
tmp=min(x/(x/i),y/(y/i));
//bwl+=sm(x/i,y/i)*(mus[tmp]-mus[i-1]);
bwl+=(x/i)*(y/i)*(mus[tmp]-mus[i-1]);
}
return bwl;
}
int main(){freopen("ddd.in","r",stdin);
gtmu();
cin>>n;
while(n --> 0){ //趋向于
//cin>>n1>>n2>>m1>>m2>>m;
n1=rd(),n2=rd(),m1=rd(),m2=rd(),m=rd();
printf("%d\n",cclt(n2/m,m2/m)+cclt((n1-1)/m,(m1-1)/m)-cclt((n1-1)/m,m2/m)-cclt(n2/m,(m1-1)/m));
//注意-1
}
return 0;
}