1. 程式人生 > >bzoj1101/洛谷P3455 [POI2007]ZAP-Queries

bzoj1101/洛谷P3455 [POI2007]ZAP-Queries

之前看了但是沒有理解的莫比烏斯函式現在找題目練練手。。。

寒假裡面看的當時理解不了就沒有多管,導致現在寫預處理函式很懵逼。。

題意:給定n,m,d,求出1<=x<=n,1<=y<=m中使得gcd(x,y)=d的x,y的對數

最簡單的方法,O(n²)的暴力列舉就不用說了,但是這個資料範圍肯定是過不了的。。

那麼我們設函式f(d)為使得gcd(x,y)=d的x,y的對數

設函式F(d)為d可以整除gcd(x,y)的情況

那麼顯然有(仔細想一下或者手玩一下)

反演之後就是 

那麼我們先預處理μ的在範圍內的值然後每次求一下F(d),那麼就可以在根號時間內求出f(d)

程式碼:

在洛谷裡面如果計算的那裡不寫成函式的話就過不了。。也不知道怎麼回事就是TLE

//Decision's template  
#include<cstdio>  
#include<cstring>  
#include<iostream>  
#include<cstdlib>  
#include<algorithm>  
#include<cmath>  
using namespace std;  
  
#define DP_maxn 16  
#define maxn 100000  
#define INF 10000007  
#define mod 1000000007  
#define mst(s,k) memset(s,k,sizeof(s))  
  
typedef long long ll;  
  
struct Edge{  
    int from,to,dist;  
    Edge(int u,int v,int d):from(u),to(v),dist(d){}  
};  
  
/*---------------------------template End----------------------------*/  
  
int n,a,b,d,ans,mu[maxn],vis[maxn],p[maxn],top = 0,x = 0,i,j;  
  
void mobius()  
{  
    int N = 50000;  
    mu[1]=1;  
    for(i=2;i<=N;++i)  
    {  
        if(!vis[i]) { mu[i]=-1;p[++top]=i; }  
        for(j=1;(n=i*(x=p[j]))<=N;j++)  
        {  
            vis[n]=1;  
            if(i%x) mu[n]=mu[i]*mu[x];  
            else break;  
        }  
    }  
    for(i=2;i<=N;++i) mu[i]+=mu[i-1];  
}  
  
ll cal(int n,int m)  
{  
    ll ans = 0;  
    top = min(a,b);  
    i = 1;  
    for(i = 1;i<=top;i = j+1)  
    {  
        j = min(a/(a/i),b/(b/i));  
        ans += (ll)(mu[j]-mu[i-1])*(a/i)*(b/i);  
    }  
    return ans;  
}  
  
int main()  
{  
    //ios::sync_with_stdio(false);  
    //freopen("std.in","r",stdin);  
    //freopen("std.out","w",stdout);  
    mobius();  
    cin>>n;  
    while(n--)  
    {  
        ans = 0;  
        cin>>a>>b>>d;  
        a/=d,b/=d;  
        cout<<cal(a,b)<<endl;  
    }  
    return 0;  
}