BZOJ 1101: [POI2007]Zap [莫比乌斯反演]

题意:求\(\sum\limits_{i=1}^n \sum\limits_{j=1}^m [gcd(i,j)=k]\),多组询问


简单套路一下

\[\sum_{d=1}^n \mu(d) \frac{n}{kd} \frac{m}{kd} \]

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=5e4+5;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}
 
int n, m, k;
int notp[N], p[N], mu[N];
void sieve(int n) {
    mu[1] = 1;
    for(int i=2; i<=n; i++) {
        if(!notp[i]) p[++p[0]] = i, mu[i] = -1;
        for(int j=1; j<=p[0] && i*p[j]<=n; j++) {
            notp[i*p[j]] = 1;
            if(i%p[j] == 0) {mu[i*p[j]] = 0; break;}
            mu[i*p[j]] = -mu[i];
        }
    }
    for(int i=1; i<=n; i++) mu[i] += mu[i-1];
}
ll cal(int n, int m, int k) {
    n /= k; m /= k;
    if(n > m) swap(n, m);
    ll ans=0; int r;
    for(int i=1; i<=n; i=r+1) {
        r = min(n/(n/i), m/(m/i));
        ans += (ll)(mu[r] - mu[i-1]) * (n/i) * (m/i);
    }
    return ans;
}
int main() {
    //freopen("in","r",stdin);
    sieve(N-1);
    int T=read();
    while(T--) {
        n=read(); m=read(); k=read();
        printf("%lld\n", cal(n, m, k));
    }
}
posted @ 2017-03-24 15:16  Candy?  阅读(201)  评论(0编辑  收藏  举报