链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695
题意:有两个区间[a,b]和[c,d]求这两个区间中gcd(i,j)==k的对数, i ∈ \in ∈[a,b],j ∈ \in ∈[c,d]。
不能重复,数对(i,j)和(j,i)视为相同。
另一道差不多题目的题解参考:https://blog.csdn.net/qq_40942372/article/details/88656852
这道题还特别说了是区间[1,a]和[1,b]型的,不同的还有求的是gcd==k以及不能重复。
直接到这一步 f ( n ) = ∑ ( u ( d / n ) ∗ g ( d ) ) f(n)=∑(u(d/n)∗g(d)) f(n)=∑(u(d/n)∗g(d))n|d
这里n=k,所以 求 f ( k ) = ∑ ( u ( d / k ) ∗ g ( d ) ) f(k)=\sum(u(d/k)*g(d)) f(k)=∑(u(d/k)∗g(d))
g ( d ) g(d) g(d)也一样,还是等于 ( a / d ) ∗ ( b / d ) (a/d)*(b/d) (a/d)∗(b/d)
g(d)为gcd(i,j)%d==0的对数,也就是说i和j都是d的倍数,那么就看i有几个j有几个,相乘就是g(d)了
i和j的个数很显然就是上界/d,所以g(d)=(a/d)∗(b/d)最后f(k)=∑(u(d/k)∗(a/d)∗(b/d)),在范围内枚举d(d为k的倍数),当然也可以直接把上界全部除k,这样的话f(k)=∑(u(d/k)∗(a/d)∗(b/d))就变成了f(1)=∑(u(d)∗(a/k/d)∗(b/k/d)),枚举可以方便一点。
如何处理重复:稍微找几个例子如 1 3 1 5 1
(1,1)(1,2)(1,3)(1,4)(1,5)
(2,1)(2,3)(2,5)
(3,1)(3,2)
重复的数字都是包含在上界较小的那个区间中的,不会超过这个区间的右端点。想一想,如果在两个小区间求gcd(i,j)==k的结果会是什么,不就是这些重复的再加上一个么,向上面例子的话就是
(1,1)(1,2)(1,3)
(2,1)(2,3)
(3,1)(3,2)
重复的数量就是(两个小区间求gcd(i,j)==k的结果-1)/2
最终的答案就是之前算出来的减去这部分
优化还是一样的分块优化,可以看那篇
小坑点:k可以等于0,特判一下
参考代码:
#include
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int maxn=1e5;
int mu[N],prime[N];
bool vis[N];
void work_mobius()
{
mu[1]=1;
int cnt=0;
for(int i=2;i<=maxn;i++)
{
if(!vis[i])
{
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&i*prime[j]<=maxn;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]) mu[i*prime[j]]=-mu[i];
else
{
mu[i*prime[j]]=0;
break;
}
}
}
for(int i=2;i<=maxn;i++)
mu[i]=mu[i-1]+mu[i];
}
ll solve(int b,int d){
ll ret=0;
for(int l=1,r=0;l<=min(b,d);l=r+1){
r=min(b/(b/l),d/(d/l));
ret+=1ll*(mu[r]-mu[l-1])*(b/l)*(d/l);
}
return ret;
}
int main(){
work_mobius();
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++){
int a,b,c,d,k;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k==0){
printf("Case %d: 0\n",ca);
continue;
}
if(b>d)swap(b,d);//这道题目可以不需要容斥,这样写即使左端点不是1也是对的
ll ans=solve(b/k,d/k)-solve((a-1)/k,d/k)-solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k);
ll duo=solve(b/k,b/k)-solve((a-1)/k,b/k)-solve(b/k,(a-1)/k)+solve((a-1)/k,(a-1)/k);
printf("Case %d: %lld\n",ca,ans-duo/2);
}
return 0;
}