还是ZZK大佬的讲解最平易近人了QwQ……
看到区间果断容斥(来自ZZK的教诲),令
A=⌊ak⌋
,
B=⌊bk⌋
,又是推公式的环节:
∑i=1a∑j=1b[(i,j)=k]=∑i=1A∑j=1B[(i,j)=1]=∑i=1A∑j=1Be((i,j))
我们知道 e=μ∗1 ,于是式子可以变成
∑i=1A∑j=1B∑d|(i,j)μ(d)=∑d=1min(A,B)μ(d)⌊Ad⌋⌊Bd⌋
然后就是喜闻乐见的数论分块了。
附上AC代码:
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=5e4+10;
int t,a,b,c,d,k,miu[N],p[N],num;
bool bo[N];
inline char nc(void){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=(a<<3)+(a<<1)+c-'0',c=nc());
return (void)(a*=f);
}
inline void prime(int n){
miu[1]=1;
for (int i=2; i<=n; ++i){
if (!bo[i]) p[++num]=i,miu[i]=-1;
for (int j=1; j<=num&&p[j]*i<=n; ++j){
bo[p[j]*i]=1;
if (i%p[j]==0) {miu[p[j]*i]=0;break;}
else miu[p[j]*i]=-miu[i];
}
}
for (int i=2; i<=n; ++i) miu[i]+=miu[i-1];
return;
}
inline ll calc(int a,int b){
ll ret=0;a/=k,b/=k;
for (int l=1,r; l<=a&&l<=b; l=r+1)
r=min(a/(a/l),b/(b/l)),ret+=1ll*(miu[r]-miu[l-1])*(a/l)*(b/l);
return ret;
}
int main(void){
for (prime(5e4),read(t); t; --t){
read(a),read(b),read(c),read(d),read(k),--a,--c;
printf("%lld\n",calc(b,d)-calc(a,d)-calc(b,c)+calc(a,c));
}
return 0;
}
弱弱的附在最后:话说还有一道比这题要水一点的题目,哇!双倍经验!,题目传送门。
这题不用容斥,直接可以出答案的。
附上AC代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=5e4+10;
int t,a,b,k,p[N],miu[N],num;
bool bo[N];
inline void get(int n){
miu[1]=1;
for (int i=2; i<=n; ++i){
if (!bo[i]) p[++num]=i,miu[i]=-1;
for (int j=1; j<=num&&p[j]*i<=n; ++j){
bo[p[j]*i]=1,miu[p[j]*i]=-miu[i];
if (i%p[j]==0) {miu[p[j]*i]=0;break;}
}
}
for (int i=2; i<=n; ++i) miu[i]+=miu[i-1];
return;
}
inline int calc(int a,int b){
int ret=0;
for (int l=1,r; l<=a&&l<=b; l=r+1) r=min(a/(a/l),b/(b/l)),ret+=1ll*(miu[r]-miu[l-1])*(a/l)*(b/l);
return ret;
}
int main(void){
for (get(5e4),scanf("%d",&t); t; --t){
scanf("%d%d%d",&a,&b,&k);
printf("%d\n",calc(a/k,b/k));
}
return 0;
}