BZOJ 2820: YY的GCD [莫比烏斯反演]【學習筆記】


2820: YY的GCD

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 1624  Solved: 853
[Submit][Status][Discuss]

Description

神犇YY虐完數論后給傻×kAc出了一題給定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)為質數的(x, y)有多少對kAc這種
傻×必然不會了,於是向你來請教……多組輸入

Input

第一行一個整數T 表述數據組數接下來T行,每行兩個正整數,表示N, M

Output

T行,每行一個整數表示第i組數據的結果

Sample Input

2
10 10
100 100

Sample Output

30
2791

HINT

T = 10000

N, M <= 10000000


 

和bzoj2705很像http://www.cnblogs.com/candy99/p/6200745.html

但是n和m不同,不能使用直接歐拉函數的方法

 

參考:http://blog.csdn.net/acdreamers/article/details/8542292 && popoqqq課件

和上一題相同的函數:

  為滿足的對數

  為滿足的對數

顯然,反演后得到

 

可以枚舉每一個質數,套用上一題的做法,p相當於k,d*p也就是p的倍數了...很像上一題我WT1中的式子

 

其實d只要枚舉到min(n,m)/p

然而復雜度承受不了,大約n/logn*sqrt(n)

我們設,那么繼續得到

 

為什么這么做呢?因為這樣之后發現F函數與p和d無關了,(要不然枚舉p和d也是枚舉了T)

可以提到前面,剩下的那一塊可以處理前綴和做到O(1),前面再用除法分塊,做到O(sqrt(n))

 

WT:

如何求g(T)=Σ{p|T && isprime(p)}miu(T/p)

法1.

只需要枚舉每個素數,將他的倍數的g更新就可以了

由於有1/1+1/2+1/3+...+1/n=O(logn)這個結論 因此每個質數枚舉時是均攤O(logn)的(*n后好想,是nlogn,但是質數只有n/logn個)

而質數恰好有O(n/logn)個 因此暴力枚舉就是O(n)的

法2.

線性篩

g[i*p[j]]

當p[j]|i時結果顯然為miu(i)

否則考慮mu(i*p[j]/pp),當p[j]=pp時為mu[i],p[j]!=pp時的所有的和就是-g(i),所以總的結果為mu(i)-g(i)

 枚舉質數 4176ms

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e7+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;
bool notp[N];
int p[N],mu[N],g[N];
void sieve(){
    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 j=1;j<=p[0];j++)
        for(int i=p[j];i<N;i+=p[j])
            g[i]+=mu[i/p[j]];
    for(int i=1;i<N;i++) g[i]+=g[i-1];
}
ll cal(int n,int m){
    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)(g[r]-g[i-1])*(n/i)*(m/i);
    }
    return ans;
}
int main(int argc, const char * argv[]) {
    sieve();
    int T=read();
    while(T--){
        n=read();m=read();
        printf("%lld\n",cal(n,m));
    }
    return 0;
}

 線性篩:3328ms

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e7+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;
bool notp[N];
int p[N],mu[N],g[N];
void sieve(){
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!notp[i]) p[++p[0]]=i,mu[i]=-1,g[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;
                g[i*p[j]]=mu[i];
                break;
            }
            mu[i*p[j]]=-mu[i];
            g[i*p[j]]=mu[i]-g[i];
        }
    }
    for(int i=1;i<N;i++) g[i]+=g[i-1];
}
ll cal(int n,int m){
    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)(g[r]-g[i-1])*(n/i)*(m/i);
    }
    return ans;
}
int main(int argc, const char * argv[]) {
    sieve();
    int T=read();
    while(T--){
        n=read();m=read();
        printf("%lld\n",cal(n,m));
    }
    return 0;
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM