题意:在[1,b]找一个x,在[1,d]之间找一个y,使得gcd(x,y)= k,其中b,d,k<10^5;k可能=0;并且(x,y)与(y,x)视作一种情况。求(x,y)的对数;
算法:我们假设x<y,(否则swap)一定存在一个n<m使得 x=nk,y=mk。并且n与m互质。所以转换成求[1,b/k]内选择一个数a和[1,d/k]内选择一个数b,有多少对(a,b)互质。
很简单把[1,,d/k]区间分为两段[1,b/k]和[b/k+1,d/k]对于前一段区间直接区间内欧拉函数累加和即可。对于后一段区间需要用到容斥原理求某个数与n个数有多少个是互质的。正难则反,求有多少个数与某个数不互质,b/k-这个个数即可。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 100009
typedef long long LL;
int prime[N],euler[N],vis[N],top;
int have[N];
LL sum[N];
int a,b,c,d,k;
void getEuler(){
memset(vis,0,sizeof(vis));
euler[1] = top = 1;
for(int i = 2;i<N;i++){
if(vis[i]==0){
prime[top++] = i;
euler[i] = i - 1;
}
for(int j = 1;j<top;j++){
if((LL)i*prime[j]>=N) break;
vis[i*prime[j]] = 1;
if(i%prime[j]==0){
euler[i*prime[j]] = euler[i]*prime[j];
break;
}
else euler[i*prime[j]] = euler[i]*(prime[j]-1);
}
}
}
void setHave(int y){
memset(have,0,sizeof(have));
int top1 = 0;
int num[10009];
for(int x = 1;x<=y;x++){
top1 = 0;
int tem = x;
for(int i = 1;i<top&&prime[i]*prime[i]<=tem;i++){
if(tem%prime[i]==0) num[top1++] = prime[i];
while(tem%prime[i]==0) tem/=prime[i];
}
if(tem!=1) num[top1++] = tem;
for(int i = 1;i<(1<<top1);i++){
tem = 1;
for(int j = 0;j<top1;j++){
if((i&(1<<j))>0) tem *= num[j];
}
have[tem]++;
}
}
}
int solve(int x){
int top1 = 0,ans = 0;
int num[10009];
int tem = x;
for(int i = 1;i<top&&prime[i]*prime[i]<=tem;i++) {
if(tem%prime[i]==0) num[top1++] = prime[i];
while(tem%prime[i]==0) tem/=prime[i];
}
if(tem!=1) num[top1++] = tem;
int k = 0;
for(int i = 1;i<(1<<top1);i++) {
tem = 1;k = 0;
for(int j = 0;j<top1;j++){
if((i&(1<<j))>0){
tem *= num[j];
k++;
}
}
if(k%2==1) ans += have[tem];
else ans -= have[tem];
}
return ans;
}
void init(){
getEuler();
sum[0] = 0;
for(int i = 1;i<N;i++)
sum[i] = sum[i-1] + euler[i];
}
int main(){
int T,cas=1;
init();
//freopen("Test.txt","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k==0){//这里需要判断k==0情况 错了一发
printf("Case %d: 0\n",cas++);
continue;
}
if(b>d) swap(b,d);
LL ans = sum[b/k];//第一次提交这里没有用LL型 错了一发
setHave(b/k);
for(int i = b/k+1;i<=d/k;i++)
ans += (b/k - solve(i));
printf("Case %d: %I64d\n",cas++,ans);
}
return 0;
}