BZOJ1011 莫比乌斯反演(基础题

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=1101

 

【题目大意】

    求[1,n][1,m]内gcd=k的情况

 

【题解】

    考虑求[1,n][1,m]里gcd=k

    等价于[1,n/k][1,m/k]里gcd=1

    考虑求[1,n][1,m]里gcd=1

    结果为sum(mu[d]*(n/d)*(m/d))

    预处理O(n^1.5)

    由于n/d只有sqrt(n)种取值,所以可以预处理出miu[]的前缀和 询问时分段求和

 

【代码】

 
#include<bits/stdc++.h>
#define  ll long long
using namespace std;
const int N = 1e5 + 5;
int t;
//线性筛法求莫比乌斯函数
bool vis[N + 10];
int pri[N + 10];
int mu[N + 10];
int sum[N];
 
void mus() {
    memset(vis, 0, sizeof(vis));
    mu[1] = 1;
    int tot = 0;
    for (int i = 2; i < N; i++) {
        if (!vis[i]) {
            pri[tot++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < tot && i * pri[j] < N; j++) {
            vis[i * pri[j]] = 1;
            if (i % pri[j] == 0) {
                mu[i * pri[j]] = 0;
                break;
            }
            else  mu[i * pri[j]] = -mu[i];
        }
    }
    sum[1]=1;
    for(int i=2;i<N;i++) sum[i]=sum[i-1]+mu[i];
}
int n,m,k;
ll cal(int x,int y){
    int ma=min(x,y);
    ll ans=0;
    for(int i=1,j;i<=ma;i=j+1){
        j=min(x/(x/i),y/(y/i));
        if(j>=ma) j=ma;
        ans+=(sum[j]-sum[i-1])*(x/i)*(y/i);//此区间内x/i与y/i均为定值
    }
    return ans;
}
int main() {
    mus();
    scanf("%d",&t);
    for(int i=0;i<t;i++){
        cin>>n>>m>>k;
        cout<<cal(n/k,m/k)<<endl;
    }
    return 0;
}

 

posted @ 2018-09-02 03:00  thges  阅读(147)  评论(0编辑  收藏  举报