bzoj2820YY的GCD;BZOJ2693jzptab

来源:互联网 发布:已备案域名接入阿里云 编辑:程序博客网 时间:2024/06/16 19:49

前言

除了说都是数论,都是莫比乌斯反演。此外,他们都用到了换元的思想。

bzoj2820YY的GCD

题意

求有多少数对x,y满足1xn,1ym,gcd(xy)为质数
1n,m1e7,T1e4

初步方案

ans=pmin(n,m)d=1min(n,m)μ(d)npdmpd

显然会TLE

优化

我们令pd=T,则可以得到

ans=T=1min(n,m)nTmTp|Tpμ(Tp)

用分块的方法,我们可以用n的时间求出前半部分
在线性筛法中,我们可以求出后半部分

代码

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;long long miu[10000005];long long sum[10000005];bool vis[10000005];long long prime[10000005];void shai(){    long long cnt=0;    miu[1]=1;    for(long long i=2;i<=10000000;i++){        if(!vis[i]){            prime[++cnt]=i;            miu[i]=-1;        }        for(long long j=1;j<=cnt&&i*prime[j]<=10000000;j++){            vis[i*prime[j]]=1;            if(i%prime[j]==0){                miu[i*prime[j]]=0;                break;            }            else                miu[i*prime[j]]=-miu[i];        }    }    for(long long i=1;i<=cnt;i++)        for(long long j=prime[i];j<=10000000;j+=prime[i])            sum[j]+=miu[j/prime[i]];    for(long long i=1;i<=10000000;i++)         sum[i]+=sum[i-1];}long long f(long long n,long long m){    if(n>m)        swap(n,m);    long long ans=0,last;    for(long long i=1;i<=n;i=last+1){        last=min(n/(n/i),m/(m/i));        ans+=(sum[last]-sum[i-1])*(n/i)*(m/i);    }    return ans;}int main(){    shai();    long long t;    scanf("%lld",&t);    while(t--){        long long n,m;        scanf("%lld%lld",&n,&m);        printf("%lld\n",f(n,m));    }}

BZOJ2693jzptab

题意

加强版BZOJ2154。

i=1nj=1mlcm(i,j)

1n,m10000.1e8+9

初步方案

ans=i=1nj=1mlcm(i,j)=i=1nj=1mijgcd(i,j)

f(x,y,k)=1ix1jygcd(i,j)=kij

ans=d=1min(n,m)d2f(nd,md,1)d=d=1min(n,m)df(nd,md,1)

sum(x,y)=1ix1jyij=x(x+1)2y(y+1)2

F(x,y,k)=1ix1jyk|gcd(i,j)ij=k2sum(nk,mk)=k|df(i,j,d)

由于
F(i,j,k)=k|df(i,j,k)

所以,由莫比乌斯反演,有
f(x,y,k)=k|dμ(dk)F(x,y,d)

f(x,y,1)=k=1min(x,y)μ(k)F(x,y,k)=k=1min(x,y)μ(k)k2sum(xkyk)

分块优化。时间复杂度O(n)

代码1(TLE)

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;long long mobiwuzi[10000005];long long sum[10000005];bool vis[10000005];long long prime[10000005];long long mod=20101009;inline long long SUM(long long x,long long y){    return (x*(x+1)/2)%mod*((y*(y+1)/2)%mod)%mod;}void shai(long long t){    long long cnt=0;    mobiwuzi[1]=1;    for(long long i=2;i<=t;i++){        if(!vis[i]){            prime[++cnt]=i;            mobiwuzi[i]=-1;        }        for(long long j=1;j<=cnt&&i*prime[j]<=t;j++){            vis[i*prime[j]]=1;            if(i%prime[j]==0){                mobiwuzi[i*prime[j]]=0;break;            }            else                mobiwuzi[i*prime[j]]=-mobiwuzi[i];        }    }    for(long long i=1;i<=t;i++){        sum[i]=(sum[i-1]+mobiwuzi[i]*i*i)%mod;    }}long long f(long long x,long long y){    if(x>y)        swap(x,y);    long long ans=0,last;    for(long long i=1;i<=x;){        last=min(x/(x/i),y/(y/i));        ans=(ans+(((sum[last]-sum[i-1])%mod)*SUM(x/i,y/i))%mod+mod)%mod;        i=last+1;    }    return ans;}void solve(long long n,long long m){    if(n>m)        swap(n,m);    long long ans=0,last;    for(long long i=1;i<=n;){        last=min(n/(n/i),m/(m/i));        ans=(ans+((((i+last)*(last-i+1)/2)%mod)*(f(n/i,m/i)%mod))%mod+mod)%mod;        i=last+1;    }    //while(ans<0) ans+=mod;    printf("%lld\n",ans);}int main(){    //while(1){    long long n,m;    scanf("%lld%lld",&n,&m);    shai(max(n,m));    solve(n,m);    //}}

优化

由上,可有

ans=d=1min(n,m)di=1min(n,m)i2μ(i)sum(ndi,mdi)

D=di
ans=D=1min(n,m)sum(nDmD)i|DDii2μ(i)

用分块的方法,我们可以用n的时间求出前半部分
在线性筛法中,我们可以求出后半部分
没错,又是这句话

代码2(AC)

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;long long h[10000005];long long sum[10000005];bool vis[10000005];long long prime[10000005];long long mod=100000009;long long SUM(long long x,long long y){    return ((x*(x+1)/2)%mod*((y*(y+1)/2)%mod))%mod;}void shai(long long t){    long long cnt=0;    h[1]=1;    for(long long i=2;i<=t;i++){        if(!vis[i]){            prime[++cnt]=i;            h[i]=(i-i*i%mod+mod)%mod;        }        for(long long j=1;j<=cnt&&i*prime[j]<=t;j++){            vis[i*prime[j]]=1;            if(i%prime[j]==0){                h[i*prime[j]]=prime[j]*h[i]%mod;                break;            }            else                h[i*prime[j]]=h[prime[j]]*h[i]%mod;        }    }    for(long long i=1;i<=t;i++)        sum[i]=sum[i-1]+h[i];}long long f(long long n,long long m){    if(n>m)        swap(n,m);    long long ans=0,last;    for(long long i=1;i<=n;i=last+1){        last=min(n/(n/i),m/(m/i));        ans+=SUM(n/i,m/i)*((sum[last]-sum[i-1])%mod)%mod;        ans=(ans+mod)%mod;    }    while(ans<0) ans+=mod;    return ans;}int main(){    int t;    scanf("%d",&t);    shai(10000000);    while(t--){    long long n,m;    scanf("%lld%lld",&n,&m);    printf("%lld\n",f(n,m));    }}

总结

一堆函数乱搞
莫比乌斯反演
线性筛法预处
分块优化处理

原创粉丝点击