这道题目就是求
∑x=ab∑y=cd[gcd(x,y)=k]
。其中数据范围全是
5×105
级别
这个显然就是莫比乌斯反演。我们可以先用前缀和转化一下,然后题目就变成了求
f(i)=∑x=1n∑y=1m[gcd(x,y)=i]
然后利用莫比乌斯反演我们马上就能够得到对于
F(i)=∑x=1n∑y=1m[i|gcd(x,y)]=[ni][mi]
有
f(i)=∑i|dμ(di)F(d)
那么就可以做了。
但是这只是第一步,我们马上又发现,这样时间空间好像都会爆……
但是我们又发现,由于 F(i) 里面写的是整除,所以实际上不同的值的个数只有 2(n−−√+m−−√) 个,于是可以分段存下相应的 F(d) ,然后根据乘法分配律,我们对于每一段相同 F(d) 计算的时候,只需要利用前缀和搞一搞莫比乌斯函数区间和就好了!
于是时间复杂度就是 O(nn−−√) 了,代码也十分好写,只是要非常注意边界情况……
这题因为太水了我就不写 μ(d) 的线筛了,代码如下:
# include <algorithm>
# include <cstdio>
using namespace std;
const int maxn = 50010;
const int maxs = 50000;
int mu[maxn]={0,1};
bool nprime[maxn];
int prime[maxn],num;
int pre[maxn];
int k;
void getprime(){
for (int i=2;i<=maxs;++i){
if (!nprime[i]){ prime[num++]=i; mu[i]=-1; }
for (int j=0;j<num&&prime[j]*i<=maxs;++j){
nprime[i*prime[j]] = true;
if (i%prime[j] == 0){
mu[i*prime[j]] = 0;
break;
}
else mu[i*prime[j]] = -mu[i];
}
}
}
int getnum(int x,int y){
int last = 0;
int ans = 0;
x /= k; y /= k;
for (int i=1;i<=min(x,y);i=last+1){
last = min(x/(x/i),y/(y/i));
ans += (pre[last]-pre[i-1])*(x/i)*(y/i);
}
return ans;
}
int main(){
getprime();
for (int i=1;i<=maxs;++i) pre[i] = pre[i-1] + mu[i];
int T; scanf("%d",&T);
while (T--){
int a,b,c,d;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
--a; --c; //Preffix
printf("%d\n",getnum(b,d)-getnum(a,d)-getnum(c,b)+getnum(a,c));
}
return 0;
}