【SPOJ7001】Visible Lattice Points-莫比乌斯反演+分块

来源:互联网 发布:windows 2000系统 编辑:程序博客网 时间:2024/06/05 08:43

测试地址:Visible Lattice Points
题目大意:在三维空间中,我们说一个点是可见的当且仅当它与点(0,0,0)连成的线段不经过任何其他坐标为整数的点。有T(T50)组询问,每组询问给出一个参数N,意为询问在所有点(x,y,z)(0x,y,zN,1N1106)中,有多少个可见的点,其中x,y,z为整数。特别地,(0,0,0)不算作可见的点。
做法:这一道题是POJ3090的加强版,从二维扩展到了三维,我写的POJ3090题解在此。而这题就不能简单地使用欧拉函数的性质来解决问题了,而需要用莫比乌斯反演来解决。
将点分为三种情况:1.x,y,z均不为0。2.x,y,z中只有一个是0。3.x,y,z中只有一个不是0。对于第一种情况,很容易看出当gcd(x,y,z)=1时,点(x,y,z)是可见的,那么我们就是要求下面这个式子的值(注意,下文中方括号[]表示如果括号内的式子为真,值为1,否则值为0):

x=1Ny=1Nz=1N[gcd(x,y,z)=1]

显然暴力求这个式子会炸,这时候我们就要用到一个莫比乌斯函数的性质:d|nμ(d)=[n=1],这个性质使得我们可以把上式化成:
Nx=1Ny=1Nz=1d|gcd(x,y,z)μ(d)
分析第四个求和号下面的条件,显然d|gcd(x,y,z)的充要条件是d|xd|yd|z,那么上式就可以化成:
Nd=1μ(d)1xNd|x1yNd|y1zNd|z1
这个式子等价于:
Nd=1μ(d)(1xNd|x1)×(1yNd|y1)×(1zNd|z1)
那么这个式子显然等于:
Nd=1μ(d)Nd3
这就是第一种情况的答案了。第二种情况就是要分别求gcd(x,y)=1,gcd(y,z)=1,gcd(x,z)=1的点数,式子的推导和上面比较类似,这里就不再赘述了。第三种情况显然只有(0,0,1),(0,1,0),(1,0,0)三个点。这样我们就找到了一个可以O(N)计算单个询问答案的方法。但是O(TN)的总复杂度对于严苛的时间限制好像有点拙计,需要想办法优化。注意到Nd的值不会超过2N种,而且对于同一个Nd的值,满足条件的d是一个连续的区间,这启发我们利用分块思想优化,问题转变为如何求Nd值相等的区间。思考对于任意一个d,求满足Nd=Np的最大的p。令q=Nd,那么N=dq+r0=pq+r1,我们知道在q相等的情况下,r1越逼近0p就越大,所以最大的p=Nq=NNd。那么我们再预处理出μ(i)的前缀和就可以把单个询问的时间复杂度加速到O(N),处理询问的总时间复杂度为O(TN),可以通过此题。
以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#define ll long longusing namespace std;int T;ll N,mu[1000010],sum[1000010];bool prime[1000010]={0};void calc_mu(){  N=1000000;  for(int i=1;i<=N;i++) mu[i]=1;  for(int i=2;i<=N;i++)    if (!prime[i])    {      for(int j=1;i*j<=N;j++)      {        mu[i*j]*=-1;        if (j>1) prime[i*j]=1;        if (!(j%i)) {mu[i*j]=0;continue;}      }    }  sum[0]=0;  for(int i=1;i<=N;i++) sum[i]=sum[i-1]+mu[i];}int main(){  scanf("%d",&T);  calc_mu();  while(T--)  {    scanf("%lld",&N);    ll ans=3,last;    for(ll d=1;d<=N;d=last+1)    {      last=N/(N/d);      ans+=((N/d)+3)*(N/d)*(N/d)*(sum[last]-sum[d-1]);    }    printf("%lld\n",ans);  }  return 0;}
阅读全文
0 0
原创粉丝点击