【BZOJ2820】GCD

题目

链接:BZOJ2820
(权限题,洛谷P2257

解法:莫比乌斯反演

一道较简单的莫比乌斯反演,推推式子就OK了。

A n s = i = 1 n j = 1 m p [ gcd ( i , j ) = p ] = p i n p j m p [ gcd ( i , j ) = 1 ] = p i n p j m p d | gcd ( i , j ) μ ( d ) = p d = 1 min ( n p , m p ) μ ( d ) i n d p j m d p 1 = p d = 1 min ( n p , m p ) n d p m d p μ ( d )

单次询问时间复杂度: O ( π ( n ) min ( n , m ) ) ,一眼看过去,诶,能过,突然就看到了多组数据 T 10 5 。。凉了呀

发现式中有迭代变量的乘积,于是设 t = d p ,就有:

A n s = p d = 1 min ( n p , m p ) n d p m d p μ ( d ) = t = d p n t m t p μ ( t p )

在筛出素数后,迭代求出 p μ ( t p ) 的前缀和,
即可使用分块求解。

时间复杂度: O ( T ( π ( n ) log π ( n ) + n ) )

AC代码

#include<iostream>
#include<cstdio>
#include<vector>

using namespace std;

const int N=10000000;
vector<int> prime;
bool check[10000001];
int T,n,m;
long long summu[10000001],mu[10000001];

void sieve(){
    mu[1]=1ll;
    for(int i=2;i<=N;++i){
        if(!check[i]){
            prime.push_back(i);
            mu[i]=-1ll;
        }
        for(int j:prime){
            if(i*j>N)break;
            mu[i*j]=-mu[i];
            check[i*j]=true;
            if(i%j==0){
                mu[i*j]=0;
                break;
            }
        }
    }
    for(int i:prime)for(int j=1;i*j<=N;++j)summu[i*j]+=mu[j];
    for(int i=1;i<=N;++i)summu[i]+=summu[i-1];
}

void solve(int n,int m){
    if(n>m)swap(n,m);
    int lst;
    long long res=0ll;
    for(int i=1;i<=n;i=lst+1){
        lst=min(n/(n/i),m/(m/i));
        res+=(summu[lst]-summu[i-1])*(n/i)*(m/i);
    }
    printf("%lld\n",res);
}

int main(){
    scanf("%d",&T);
    sieve();
    for(int i=1;i<=T;++i){
        scanf("%d%d",&n,&m);
        solve(n,m);
    }
}

猜你喜欢

转载自blog.csdn.net/ezoixx174/article/details/81572022