1. 程式人生 > >[SPOJ VLATTICE]Visible Lattice Points 數論 莫比烏斯反演

[SPOJ VLATTICE]Visible Lattice Points 數論 莫比烏斯反演

string rst ase 計算 mod visible font sample poj

7001. Visible Lattice Points

Problem code: VLATTICE

Consider a N*N*N lattice. One corner is at (0,0,0) and the opposite one is at (N,N,N). How many lattice points are visible from corner at (0,0,0) ?

A point X is visible from point Y iff no other lattice point lies on the segment joining X and Y.

Input :
The first line contains the number of test cases T. The next T lines contain an interger N

Output :
Output T lines, one corresponding to each test case.

Sample Input :
3
1
2
5

Sample Output :
7
19
175

Constraints :
T <= 50
1 <= N <= 1000000

題目大意

給定n*n*n的立方體,每一個整數點除(0。0。0)之外都有一盞燈(抽象理解),問能看到多少盞燈(被蓋住的燈不算)


解題思路

莫比烏斯反演/容斥原理的典型應用

用容斥原理來解釋就是三個點都能被k整除的個數乘上莫比烏斯系數,求和就可以

三個點都能被k整除的個數就是floor(n/i)^3


註意到最大數據量為1000000 直接線性處理的辦法可能TLE

而(n/i)在後面i>(n/2)的部分結果都為1 能夠省去一次次計算,直接按mu的前綴和來處理

則我們就統計同樣(n/i)的值是否出現兩次。假設出現兩次那麽我們就開始依照前綴和的方法來處理

不優化 6200ms

優化後 490ms


code

優化前

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
#include <cctype>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <list>
#include <vector>
#include <map>
#include <set>

#define sqr(x) ((x)*(x))
#define LL long long 
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define eps 1e-10
#define mod 100000007ll
using namespace std;
LL n;
LL com[1000005],pri[1000005],phi[1000005],pn,sum[1000005],mu[1000005];
LL a[1000005];
int main()
{
    memset(com ,0 ,sizeof com);
    mu[1]=1;
    for (int i=2;i<=1000000ll;i++)
    {
        if (com[i]==0)
        {
            phi[i]=i-1;
            pri[++pn]=i;
            mu[i]=-1;
            // printf("%d\n", pri[pn]);
            // system("pause");
        }
        for (int j=1;j<=pn&&pri[j]*i<=1000000ll;j++)
        {
            if (i%pri[j])
            {
                phi[i*pri[j]]=phi[i]*(pri[j]-1);
                com[i*pri[j]]=1;
                mu[i*pri[j]]=-mu[i];
            }
            else 
            {
                phi[i*pri[j]]=phi[i]*(pri[j]);
                com[i*pri[j]]=1;
                mu[i*pri[j]]==0;
                break;
            }
        }
    }
    sum[0]=0;
    for (int i=1;i<=1000000ll;i++)
        sum[i]=sum[i-1]+phi[i];
    int T;
    scanf("%d",&T);
    while (T--)
    {
        // n=1000000;
        LL ans=0;
        scanf("%lld",&n);
        for (int i=n;i;i--)
        {
            a[i]=(n/i)*(n/i)*(n/i);
            ans+=a[i]*mu[i];
        }
        printf("%lld\n",ans+(sum[n]*2+1)*3+3);
    }

    return 0;
}


優化後

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
#include <cctype>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <list>
#include <vector>
#include <map>
#include <set>

#define sqr(x) ((x)*(x))
#define LL long long 
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define eps 1e-10
using namespace std;
int mu[1000005];
int com[1000005];
int pri[1000005],pn=0;
int phi[1000005];
LL presum[1000005];
int musum[1000005];
int main()
{
	memset(com,0,sizeof com);
	presum[1]=0;
	mu[1]=1;
	phi[1]=0;
	for (int i=2;i<=1000000;i++)
 	{
  	if (com[i]==0)
  		{
   		pri[++pn]=i;
   		mu[i]=-1;
   		phi[i]=i-1;
  		}
  	for (int j=1;j<=pn&&pri[j]*i<=1000000;j++)
  	{
   	if (i%pri[j])
	   {
	    mu[i*pri[j]]=-mu[i];
	    com[i*pri[j]]=1;
	    phi[i*pri[j]]=phi[i]*(pri[j]-1);
	   }
	   else 
	   {
	    phi[i*pri[j]]=phi[i]*(pri[j]);
	    mu[i*pri[j]]=0;
	    com[i*pri[j]]=1;
	    break;
	   }
  	}
  	presum[i]=presum[i-1]+phi[i];
  	musum[i]=musum[i-1]+mu[i];
 	}
 int T;
 scanf("%d",&T);
 int a,b,c,d,k;
 while (T--)
 {
 	int n;
 	LL ans=0;
 	scanf("%d",&n);
 	int i;
 	for (i=1;i<=n;i++)
 		if ((n/i)==(n/(i+1))) break;
 		else 
 		ans+=(LL)(n/i)*(n/i)*(n/i)*mu[i];
 	for (int j=(n/i);j;j--)
 		ans+=(LL)(j)*(j)*(j)*(musum[n/(j)]-musum[n/(j+1)]);
 	ans+=(LL)presum[n]*6+6;
 	printf("%lld\n",ans);
 }
 return 0;
}


[SPOJ VLATTICE]Visible Lattice Points 數論 莫比烏斯反演