1. 程式人生 > >一些數論函式題

一些數論函式題

luogu2257 YY的GCD

答案為:
\(\sum\limits_{p\in prime}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)==p]\)
\(=\sum\limits_{p\in prime}\sum\limits_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum\limits_{j=1}^{{\lfloor\frac{m}{p}}\rfloor}[gcd(i,j)==1]\)
\(=\sum\limits_{p\in prime}\sum\limits_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum\limits_{j=1}^{{\lfloor\frac{m}{p}}\rfloor}\sum\limits_{t \mid i\land t\mid j}\mu(t)\)


\(=\sum\limits_{p\in prime}\sum\limits_{t=1}^{min(n,m)}\mu(t)\sum\limits_{i=1}^{\lfloor\frac{n}{pt}\rfloor}\sum\limits_{j=1}^{{\lfloor\frac{m}{pt}}\rfloor}1\)
\(=\sum\limits_{p\in prime}\sum\limits_{t=1}^{min(n,m)}\mu(t)\lfloor\frac{n}{pt}\rfloor\lfloor\frac{m}{pt}\rfloor\)
\(T=pt\)
原式\(=\sum\limits_{p\in prime}\sum\limits_{t=1}^{min(n,m)}\mu(t)\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\)

\(=\sum\limits_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum\limits_{p\in prime \land p\mid T}\mu(\frac{T}{p})\)
前面的\(\sum\limits_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\)就是一個整除分塊。這就需要我們求出\(\sum\limits_{p\in prime \land p\mid T}\mu(\frac{T}{p})\)的前綴合,我們列舉p然後遍歷值域就是調和級數複雜度的預處理。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int mu[10000002],prim[1000000],vis[10000002],g[10000002],sum[10000002],cnt,n[10100],m[10100],t;
long long ans;
inline void read(int &x)
{
    x=0;
    static int p;p=1;
    static char c;c=getchar();
    while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<1)+(x<<3)+(c-48);c=getchar();}
    x*=p;   
}
void getmu(int n){
    mu[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]){
            prim[++cnt]=i; 
            mu[i]=-1;
        }
        for(int j=1;i*prim[j]<=n;j++){
            vis[i*prim[j]]=1;
            if(i%prim[j]==0)break;
            else mu[i*prim[j]]=-mu[i];
        }
    }
    for(int i=1;i<=cnt;i++)
        for(int j=1;j*prim[i]<=n;j++)
            g[j*prim[i]]+=mu[j];
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+g[i];
}
int main(){
    read(t);
    int tot=0;
    for(int i=1;i<=t;i++){
        read(n[i]);read(m[i]);
        tot=max(tot,max(n[i],m[i]));
    }
    getmu(tot);
    for(int i=1;i<=t;i++){
        ans=0;
        if(n[i]>m[i])swap(n[i],m[i]);
        for(int l=1,r;l<=n[i];l=r+1){
            r=min(n[i]/(n[i]/l),m[i]/(m[i]/l));
            ans+=(long long)(n[i]/l)*(m[i]/l)*(sum[r]-sum[l-1]);
        } 
        printf("%lld\n",ans);
    }
    return 0;   
} 

[CQOI2015]選數

顯然是一道莫比烏斯反演。
設f[i]為選出n個數gcd為i的方案數。
設F[i]為選出n個數gcd為i的倍數的方案數。
把L,H都除去k,答案就是f[1](注意一點除的時候當L%k!=0是L除完k之後還要加1)。
然後有\(F[i]=\sum\limits_{i \mid d }f[d]\)
\(f[i]=\sum\limits_{i \mid d} \mu(\frac{d}{i})F[d]\)
應為我們要求的是f[1],
\(f[1]=\sum\limits_d \mu(d)F[d]\)
F怎麼求就是可以選的數的n次方:\((\lfloor\frac{H}{k}\rfloor-\lfloor\frac{L-1}{k}\rfloor)^n\)
\(\mu\)字首和用杜教篩就行了。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
#define int long long
map<int,bool>vis;
map<int,int> hsh;
const int N=2001000;
const int p=1e9+7;
int mu[N],book[N],prime[N],cnt,n,k,L,H;
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int sum(int x){
    if(x<=1000000)return mu[x];
    if(vis[x]==1)return hsh[x];
    int tmp=1;
    for(int l=2,r;l<=x;l=r+1){
        r=(x/(x/l));
        tmp=tmp-(r-l+1ll)*sum(x/l)%p;
        tmp=(tmp%p+p)%p;
    }
    vis[x]=1;hsh[x]=tmp;
    return tmp;
}
int ksm(int x,int b){
    int tmp=1;
    while(b){
        if(b&1)tmp=tmp*x%p;
        b>>=1;
        x=x*x%p;
    }
    return tmp;
}
int work(){
    int tmp=0;
    for(int l=1,r;l<=H;l=r+1){
        if(l>L-1ll)r=H/(H/l);
        else r=min(H/(H/l),(L-1ll)/((L-1ll)/l)); 
        tmp=tmp+(sum(r)-sum(l-1ll))*ksm((H/l-(L-1ll)/l),n)%p;
        tmp=(tmp%p+p)%p;
    }
    return tmp;
}
signed main(){
    pre_work();
    n=read();k=read();L=read();H=read();
    if(L%k)L=L/k+1;
    else L=L/k;
    H=H/k;
    printf("%lld",work());
    return 0;
}

BZOJ 4176 Lucas的數論

enter image description here
\((n \leq 1e9)\)f代表約數個數。
\(f(ij)=\sum\limits_{x \mid i }\sum\limits_{ y\mid j}[gcd(x,y)==1]\)
我有一個精巧的證明可惜這裡地方太小,寫不下。
(我才知道這是費馬說的,思維風暴證明費馬大定理?)
我才沒有什麼精巧的證明,我會暴力。
因為約束個數函式是極性函式,把每一個質因子分開考慮。
設ij中有\(p^k\),i中有\(p^a\),j中有\(p^b\),其中a+b=k。
然後我們驚奇的發現\(p^k\)的約數個數為k+1=a+b+1。然後\(p^a\),和\(p^b\)互質的情況有a+b+1。
然後就可以嘿嘿嘿了。
原式等於:\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{x \mid i }\sum\limits_{ y\mid j}[gcd(x,y)==1]\)
\(=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{x \mid i }\sum\limits_{y\mid j}\sum\limits_{t\mid x\land t\mid y}\mu(t)\)
\(=\sum\limits_{t=1}^{n}\mu(t)\sum\limits_{x=1}^{\lfloor\frac{n}{t}\rfloor}\lfloor\frac{n}{tx}\rfloor\sum\limits_{y=1}^{\lfloor\frac{n}{t}\rfloor}\lfloor\frac{n}{ty}\rfloor\)
\(=\sum\limits_{t=1}^{n}\mu(t) (\sum\limits_{x=1}^{ \lfloor \frac{n}{t} \rfloor} \lfloor\frac{n}{tx}\rfloor)^2\)
然後上整除分塊。
複雜度。。聽說是\(O(\sqrt{n}log\sqrt{n})\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
#define int long long
const int p=1e9+7;
const int N=1010000;
map<int,int>hsh;
map<int,bool>vis;
int mu[N],book[N],prime[N],cnt,n;
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int work(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=(tmp+(r-l+1)*(x/l)%p)%p;
    }
    return tmp;
}
int sum(int x){
    if(x<=1000000)return mu[x];
    if(vis[x])return hsh[x];
    int tmp=1;
    for(int l=2,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=tmp-sum(x/l)*(r-l+1);
        tmp=(tmp%p+p)%p;
    }
    vis[x]=1;hsh[x]=tmp;
    return tmp;
}
int calc(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        int w=work(x/l);
        tmp=tmp+(sum(r)-sum(l-1))*w%p*w%p;
        tmp=(tmp%p+p)%p;
    }
    return tmp;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
signed main(){
    n=read();
    pre_work();
    printf("%lld",calc(n));
    return 0;
}

luogu P3768 簡單的數學題

\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}ijgcd(i,j)\)
gcd不僅可以化成\(\mu\)
原式等於
\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}ij\sum\limits_{t \mid i \land t \mid j} \varphi(t)\)
為什麼?
因為\(\varphi*1\)=id
然後接著化簡。
\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}ij\sum\limits_{t \mid i \land t \mid j} \varphi(t)\)
\(=\sum\limits_{t=1}^{n}\varphi(t)*t^2\sum\limits_{i=1}^{\lfloor\frac{n}{t}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{t}\rfloor}ij\)
\(=\sum\limits_{t=1}^{n}\varphi(t)*t^2(\sum\limits_{i=1}^{\lfloor\frac{n}{t}\rfloor}i)^2\)
\(=\sum\limits_{t=1}^{n}\varphi(t)*t^2\sum\limits_{i=1}^{\lfloor\frac{n}{t}\rfloor}i^3\)
然後就可以做了。
最後一步怎麼化簡?暴力展開。。。
然後上杜教篩就行了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
#define int long long
const int p=1e9+7;
const int N=1010000;
map<int,int>hsh;
map<int,bool>vis;
int mu[N],book[N],prime[N],cnt,n;
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int work(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=(tmp+(r-l+1)*(x/l)%p)%p;
    }
    return tmp;
}
int sum(int x){
    if(x<=1000000)return mu[x];
    if(vis[x])return hsh[x];
    int tmp=1;
    for(int l=2,r;l<=x;l=r+1){
        r=x/(x/l);
        tmp=tmp-sum(x/l)*(r-l+1);
        tmp=(tmp%p+p)%p;
    }
    vis[x]=1;hsh[x]=tmp;
    return tmp;
}
int calc(int x){
    int tmp=0;
    for(int l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        int w=work(x/l);
        tmp=tmp+(sum(r)-sum(l-1))*w%p*w%p;
        tmp=(tmp%p+p)%p;
    }
    return tmp;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
signed main(){
    n=read();
    pre_work();
    printf("%lld",calc(n));
    return 0;
}

[SDOI2017]數字表格

\(\Pi_{i=1}^{n}\Pi_{j=1}^{m}f[gcd(i,j)]\)

\(=\Pi_{d=1} f[d]^{\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}[gcd(i,j)==1]}\)
\(=\Pi_{d=1} f[d]^{\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{t \mid i\land t\mid j}\mu(t)}\)
\(=\Pi_{d=1} f[d]^{\sum\limits_{i=1}^{min(n,m)}\mu(t)\lfloor\frac{n}{dt}\rfloor\lfloor\frac{m}{dt}\rfloor}\)
令T=dt
\(=\Pi_{T=1}^{min(m,n)}\Pi_{d\mid T} f[d]^{\mu(\frac{T}{d})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}\)
然後我們把\(\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\)分塊。\(\Pi_{d\mid T} f[d]^{\mu(\frac{T}{d})}\)預處理。因為調和級數複雜度可以接受。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=1010100;
int mu[N],book[N],prime[N],cnt,f[N],T,n,m,F[N],inv[N];
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
int ksm(int x,int b){
    int tmp=1;
    while(b){
        if(b&1)tmp=tmp*x%mod;
        b>>=1;
        x=x*x%mod;
    }
    return tmp;
}
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=1000000;i++){
        if(book[i]==0){
            mu[i]=-1;
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=1000000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[i*prime[j]]=-mu[i];
        }
    }
    f[0]=0;f[1]=1;
    for(int i=2;i<=1000000;i++)
        f[i]=(f[i-1]+f[i-2])%mod;
    for(int i=0;i<=1000000;i++)F[i]=1;
    for(int i=1;i<=1000000;i++)
        for(int j=1;j*i<=1000000;j++){
            int tmp;
            if(mu[j]==0)tmp=1;
            else if(mu[j]==1)tmp=f[i];
            else tmp=ksm(f[i],mod-2);
            F[i*j]=(F[i*j]*tmp)%mod;
        }
    for(int i=2;i<=1000000;i++)F[i]=(F[i-1]*F[i])%mod;
    for(int i=1;i<=1000000;i++)inv[i]=ksm(F[i],mod-2);
    inv[0]=1;
}
int work(int n,int m){
    if(n>m)swap(n,m);
    int tmp=1;
    for(int l=1,r;l<=n;l=r+1){
        r=min(m/(m/l),n/(n/l));
        tmp=(tmp*ksm(F[r]*inv[l-1]%mod,(n/l)*(m/l)))%mod;
    }
    return tmp;
}
signed main(){
    T=read();
    pre_work();
    while(T--){
        n=read(),m=read();
        printf("%lld\n",work(n,m));
    }
    return 0;
}