【莫比乌斯】 BZOJ 2301 Problem b

通道

题意:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数

思路:ans = cal(b, d) - cal(a - 1, d) - cal(b, c - 1) + cal(a - 1, c- 1),加上分块处理

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

template <class T>
inline bool rd(T &ret) {
    char c; int sgn;
    if(c = getchar() , c == EOF) return false;
    while(c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while(c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return true;
}

const int MAX_N = 50007;

int pcnt, prime[MAX_N], mu[MAX_N];
bool vis[MAX_N];

void Mobius(int n) {
    pcnt = 0;
    vis[1] = mu[1] = 1;
    for(int i = 2;i <= n; i++) {
        if(!vis[i]) prime[pcnt++] = i, mu[i] = -1;
        for (int j = 0; j < pcnt && prime[j] * i <= n; ++j) {
            vis[prime[j] * i] = true;
            if (i % prime[j] == 0) {
                mu[i * prime[j]] = 0;
                break;
            } else mu[i * prime[j]] = -mu[i];
        }
    }
}


int a, b, c, d, k;
int s[MAX_N];

ll cal(int l, int r) {
    l /= k, r /= k;
    int n = min(l, r);
    ll ans = 0;
    for (int i = 1, j = 0; i <= n; i = j + 1) {
        j = min(l / (l / i), r / (r / i));
        ans += ((ll)s[j] - s[i - 1]) * (l / i) * (r / i);
    }
    return ans;
}

int main() {
    Mobius(MAX_N - 2);
    for (int i = 1; i < MAX_N; ++i) s[i] = s[i - 1] + mu[i];
    int T; rd(T);
    while (T-- > 0) {
        rd(a), rd(b), rd(c), rd(d), rd(k);
        ll ans = cal(b, d) - cal(a - 1, d) - cal(b, c - 1) + cal(a - 1, c- 1);
        printf("%lld\n", ans); 
    }
    return 0;
}
View Code

 

posted @ 2015-08-17 21:42  Rojo ゛  阅读(...)  评论(...编辑  收藏