原题链接:https://www.luogu.org/problemnew/show/P2522
原题也只有一句话,题意简述还是免了吧
dalao说我是莫比乌斯反演界的一股清流,不明觉厉
首先是一个容斥原理,不知道dalao们都会有什么操作,反正要是让我直接算区间的话我就只好枚举了,显然直接算区间不可取
不过想到容斥原理,公式应该就很好算了,我理解的时候,心理想的是二维前缀和的模型
ans=solve(b,d)-solve(a-1,d)-solve(b,c-1)+solve(a-1,c-1);
然后就是解决solve(l,r)是怎么算的了
其实我首先并没有发觉自己做错了,写了这么一个公式:
solve(l,r)=gcd(i,j)%k==0(1<=i<=l,1<=j<=r)=(l/k)(r/k);
对于每组询问这不是O(1)出答案吗?
当然,当我写出代码来才发现问题,gcd(i,j)==2*k,3*k 的时候,也是%k==0的啊。。。
所以把公式写成:solve(l,r)=(gcd(i,j)==k)(1<=i<=l,1<=j<=r)
然后,我就只能看着标签上的莫比乌斯反演发呆了。。。
不过我突然发现,我刚写的两个公式似乎有些关系,然后拿来列在一起。。。
正式题解:
设函数f,g
g(k)=∑(gcd(i,j)%k==0)
f(k)=∑(gcd(i,j)==k)
正好满足 g(k)=∑k|df(d)
于是由莫比乌斯反演可得f(k)=∑k|dμ(k/d)g(d)
由于g(d)=(l/d)*(r/d),所以原式又可以变形为f(k)=∑d|kμ(k/d)*(l/d)*(r/d)
暴力一点的做法的话,枚举一下d,在算一下对应的莫比乌斯函数值就好了,但是在某些极端情况下,d不会很小,所以还要优化
如果d的一定范围内,l/d与r/d的值都是不会变的,其实我们只是做了(区间长度)次计算莫比乌斯函数罢了,这个地方是可以优化的。
提前算出前缀和,并且预先算出下一个会使(l/d)*(r/d)变化的d值,这样就可以用前缀和优化。
#include<cstdio> int t,a,b,c,d,k,tot; int m[50005],vis[50005],p[50005],sum[50005]; int min(int x,int y) { return x<y?x:y; } int solve(int l,int r) { l/=k,r/=k; int x=min(l,r); int ans=0,s; for(int i=1;i<=x;i=s+1) { s=min(l/(l/i),r/(r/i)); ans+=(sum[s]-sum[i-1])*(l/i)*(r/i); }
return ans; } int main() { m[1]=1; for(int i=2;i<=50000;i++) { if(!vis[i]) { m[i]=-1; p[++tot]=i; } for(int j=1;j<=tot&&i*p[j]<=50000;j++) { vis[i*p[j]]=1; if(i%p[j]==0) { m[i*p[j]]=0; break; } else m[i*p[j]]=-m[i]; } } for(int i=1;i<=50000;i++) sum[i]=sum[i-1]+m[i]; scanf("%d",&t); while(t--) { scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); int sum=solve(b,d); sum=sum-solve(a-1,d)-solve(b,c-1)+solve(a-1,c-1); printf("%d ",sum); } return 0; }