http://172.20.6.3/Problem_Show.asp?id=1518
最开始只想到了n^2的写法,肯定要超时的,所以要对求gcd的过程进行优化。
首先是前缀和容斥,很好理解。
第二个优化大致如下:
u为莫比乌斯函数,t为gcd(x,y)为i的倍数的数的个数;
满足gcd(x,y)=1的数字对的数量=sigma(1<=i<=min(x,y))u[i]*t[i];
t[i]=(x/i)*(y-i);
由小数向下取整可知有连续的i满足x/i为定值,y/i也是定值,所以可以分块计算,用u[i]的前缀和*定值,加快求gcd(x,y)=1的对数的速度。
代码
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<iostream> 6 using namespace std; 7 const int maxn=50010; 8 int n; 9 int a,b,c,d,k; 10 bool vis[maxn]={}; 11 int ur[maxn]={},su[maxn]={},sum[maxn]={},tot=0; 12 void doit(){ 13 sum[1]=1;ur[1]=1; 14 for(int i=2;i<maxn;i++){ 15 if(!vis[i]){ur[i]=-1;su[++tot]=i;} 16 for(int j=1;j<=tot&&i*su[j]<maxn;j++){ 17 int z=i*su[j];vis[z]=1; 18 if(i%su[j]==0)break; 19 ur[z]=ur[su[j]]*ur[i]; 20 } 21 sum[i]=sum[i-1]+ur[i]; 22 } 23 } 24 int getit(int x,int y){ 25 int z=0,nex=0; 26 if(x>y)swap(x,y); 27 for(int i=1;i<=x;i=nex+1){ 28 int xx=x/i,yy=y/i; 29 nex=min(x/xx,y/yy); 30 z+=(sum[nex]-sum[i-1])*xx*yy; 31 } 32 return z; 33 } 34 int main(){ 35 doit();int ans=0; 36 scanf("%d",&n); 37 while(n-->0){ 38 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 39 a-=1;c-=1; 40 a/=k;b/=k;c/=k;d/=k; 41 ans=0;ans+=getit(b,d);ans-=getit(b,c);ans-=getit(a,d);ans+=getit(a,c); 42 printf("%d\n",ans); 43 } 44 return 0; 45 }
时间: 2024-10-04 11:17:38