luogu P3455 [POI2007]ZAP-Queries

luogu P3455 [POI2007]ZAP-Queries

题目让我们求出
以下的n 是 题目中的 a, m 是 b
\[\sum_{i=1}^{n}\sum_{i = 1}^m [gcd(i,j) == d]\]
因为有\[gcd(i , j) == d\]
\[gcd(i / d , j / d) == 1\]
\(f(k)=\sum_{i=1}^n\sum_{i=1}^m[gcd(i,j)==k]\)套用
\[\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d] = \sum_{i=1}^{\frac nd}\sum_{j=1}^{\frac md}gcd(i,j)==1\]
套用反演公式2
\[g(x) = \sum_{x|y}f(y)=\sum_{x|y}\sum_{i=1}^n\sum_{i=1}^mgcd(i,j)==y=\]
\[\sum_{i=1}^n\sum_{i=1}^m\sum_{x|gcd(i,j)}=\sum_{i=1}^n\sum_{i=1}^m\sum_{x|i,x|j}=[\frac nx][\frac mx]\]
我们求
\[f(1) = \sum_{1|y}\mu(\frac y1)g(y)=\sum_{y}^{min(n,m)}\mu(y)[\frac {\frac nd}{y}][\frac {\frac md}{y}]\]
可以用前缀和加分块使一次操作达到\(O(\sqrt n)\)
所以总复杂度:\(O(T \sqrt n)\)

#include <iostream>
#include <cstdio>
#define ll long long
const int maxN = 50000 + 7;
const int N = 50000;
int mu[maxN] , prime[maxN], num;
bool vis[maxN]; 

void init() {
    mu[1] = 1;
    for(int i = 2;i <= N;++ i) {
        if( !vis[i] ) {
            prime[++ num] = i;
            mu[i] = -1;
        }
        for(int j = 1;j <= num && prime[j] * i <= N;++ j) {
            vis[prime[j] * i] = true;
            if(i % prime[j] == 0) break;
            mu[i * prime[j]] = -mu[i];
        }
    }
    for(int i = 1;i <= N;++ i) mu[i] += mu[i - 1];
    return ;
}

inline void swap(int &a,int &b) {a ^= b ^= a ^= b;return;}
inline int min(int a,int b) {return a > b ? b : a ;}

int main() {
    int T;
    scanf("%d",&T);
    init();
    while(T --) {
        int a,b,k;
        scanf("%d%d%d",&a,&b,&k);
        a /= k;b /= k;
        if(a > b) swap(a,b);
        long long ans = 0;
        for(int l = 1,r;l <= a;l = r + 1) {
            r = min(a / (a / l),b / (b / l));
            ans += 1LL * ( mu[r] - mu[l - 1] ) * (a / l) * (b / l);
        }
        printf("%lld\n",ans);
    }
}

猜你喜欢

转载自www.cnblogs.com/gzygzy/p/10122496.html