[HAOI2011]Problem b

原题链接: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\n",sum); } return 0; }

 

posted @ 2018-01-31 14:42  Excim  阅读(132)  评论(0编辑  收藏  举报