《数论11题》题集笔记

原博文网址链接:http://www.cnblogs.com/oldmanren/archive/2012/11/23/2784986.html

Problem Zero:[neerc2011]Gcd guessing game

现在有一个数x,1 ≤ x≤ n,告诉你n,每次你可以猜一个数y,如果x==y则结束,否则返回gcd(x,y),问最少只要几次就可以保证猜出答案。

求出1-n中所有素数,及其不大于n的最高幂次,另y是所有质数最高幂次的乘积,则gcd(x,y)=x,因此最少一次就可以得到结果


 

Problem One:[CQOI2007 余数之和sum]sigma(K%i),1 ≤ i ≤ n

sigma(K%i)=sigma(K-(K/i)*i),K/i可以在i连续时合并,因此只要算最多K/i的可能取值数目次就可以得到结果

结论1:K/i(1<=i<=n)最多有O(sqrt(k))个不同取值

证明:1.i=sqrt(k)时,K/i约等于sqrt(K),又K/i是递减的,因此i>sqrt(K)时,K/i的取值最多只有sqrt(K)种。即使1<=i<=sqrt(K)时K/i全都不同,总共K/i的取值也就2*sqrt(K)种,因此计算量为O(sqrt(K))

结论2:在[i,K/(K/i)]这一段区间内,K/i的值相等

类似结论:b%(a%b)

证明:设x=a%b=b/2,b-x<=b/2,所以b%x


problem2-4是关于欧拉函数的应用,略去。


 

Problem Five:[双亲数/POI Zap(多组)]求gcd(i, j) == d (i ≤ a, j ≤ b) 的对数

 

题目解析见笔记本,代码如下

int t,a,b,d;
int mu[maxn],pri[maxn],sumu[maxn],cnt;
bool not_prime[maxn];

void mobius(){//筛法求莫比乌斯函数
    mu[1]=1;
    for(int i=2;i<=maxn;i++){
        if(!not_prime[i]) pri[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++){
            not_prime[i*pri[j]]=1;
            if(i%pri[j]==0) { mu[i*pri[j]]=0;break; }
            else mu[i*pri[j]]=-mu[i];
        }
    }
    for(int i=1;i<=maxn;i++) sumu[i]=sumu[i-1]+mu[i];
}

int cal(int n,int m){
    if(n>m) swap(n,m);
    int ans=0,tmp;
    for(int i=1;i<=n;i=tmp+1){//sigma(K/i)(i>=1)分块合并求和---problem one
        tmp=min(n/(n/i),m/(m/i));
        ans+=(sumu[tmp]-sumu[i-1])*(n/i)*(m/i);
    }
    return ans;
}

int main(){
    mobius();
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&a,&b,&d);
        printf("%d\n",cal(a/d,b/d));
    }
    return 0;
}


 


Problem Six:[SPOJ PGCD/BZOJ2820YY的GCD(多组)]

求gcd(i, j)是质数, 1 ≤ i ≤ a, 1 ≤ j ≤ b 的对数

 

思路:仿照PROBLEM FIVE可以得到g函数的递推式,在筛法中可以求出g,再分块求和即可

代码如下:

#include
#include
#include
#define maxn 50010
using namespace std;

int t,a,b,d;
int mu[maxn],pri[maxn],sumg[maxn],g[maxn],cnt;
bool not_prime[maxn];

void mobius(){
    mu[1]=1;
    for(int i=2;i<=maxn;i++){
        if(!not_prime[i]) { pri[++cnt]=i,mu[i]=-1;g[i]=1; }
        for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++){
            not_prime[i*pri[j]]=1;
            if(i%pri[j]==0) { mu[i*pri[j]]=0;g[i*pri[j]]=mu[i];break; }
            else  { mu[i*pri[j]]=-mu[i];g[i*pri[j]]=mu[i]-g[i]; }
        }
    }
    for(int i=1;i<=maxn;i++) sumg[i]=sumg[i-1]+g[i];
}

int cal(int n,int m){
    if(n>m) swap(n,m);
    int ans=0,tmp;
    for(int i=1;i<=n;i=tmp+1){
        tmp=min(n/(n/i),m/(m/i));
        ans+=(sumg[tmp]-sumg[i-1])*(n/i)*(m/i);
    }
    return ans;
}

int main(){
    mobius();
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&a,&b);
        printf("%d\n",cal(a,b));
    }
    return 0;
}

Problem Seven:[NOI 2010 能量采集]sigma(gcd(i, j)), i ≤ a, j ≤ b

通过莫比乌斯变换或欧老公式求和逆变换变形后,变成sigma([n/t]*[m/t]*phi(t)),1<=t<=min(a,b)。代码如下

#include
#include
#include
#define maxn 100010
#define ll long long
using namespace std;

ll n,m;
ll prime[maxn],cnt,phi[maxn],sump[maxn];
bool not_prime[maxn];

void Phi(){
    phi[1]=1;
    for(ll i=2;i<=maxn;i++){
        if(!not_prime[i]) prime[++cnt]=i,phi[i]=i-1;//素数初始化
        for(ll j=1;j<=cnt&&i*prime[j]<=maxn;j++){
            not_prime[i*prime[j]]=1;
            if(i%prime[j]==0) { phi[i*prime[j]]=phi[i]*prime[j];break; }
            else phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
    for(ll i=1;i<=n;i++) sump[i]=sump[i-1]+phi[i];
}

ll cal(ll n,ll m){//sigma[gcd(i,j)],1<=i<=n,1<=j<=m
    if(n>m) swap(n,m);
    ll ans=0,tmp;
    for(ll i=1;i<=n;i=tmp+1){
        tmp=min(n/(n/i),m/(m/i));
        ans+=(sump[tmp]-sump[i-1])*(n/i)*(m/i);
    }
    return ans;
}

int main(){
    scanf("%lld%lld",&n,&m);
    Phi();
    printf("%lld\n",2*cal(n,m)-m*n);
    return 0;
}

 

Problem Eight:[Crash的数字表格/BZOJ2693 jzptab(多组)]

sigma(lcm(i, j)) ,i ≤ a, j ≤ b

 

利用莫比乌斯变换化简,跑了11s,也是醉了...

#include
#include
#include
#define maxn 10000100
#define ll long long
#define anti4 15075757
const ll mod=20101009;
using namespace std;

ll t,a,b;
ll n,ans,pri[maxn],cnt,f[maxn];
bool np[20101050];

void mobius(){
    f[1]=1;
    for(ll i=2;i<=n;i++){
        if(!np[i]) pri[++cnt]=i,f[i]=((1-i)%mod+mod)%mod;
        for(ll j=1;j<=cnt&&pri[j]*i<=n;j++){
            np[i*pri[j]]=1;
            if(i%pri[j]==0) { f[i*pri[j]]=f[i];break; }
            else f[i*pri[j]]=((f[i]%mod*(1-pri[j])%mod)%mod+mod)%mod;
        }
    }
    for(ll i=1;i<=n;i++) f[i]=((f[i-1]%mod+i%mod*f[i]%mod)%mod+mod)%mod;
}

ll cal(ll a,ll b){
    if(a>b) swap(a,b);
    ll ans=0,tmp;
    for(ll i=1;i<=a;i=tmp+1){
        tmp=min(a/(a/i),b/(b/i));
        ans=((ans%mod+((f[tmp]-f[i-1])%mod+mod)%mod*(a/i)%mod*(a/i+1)%mod*(b/i)%mod*(b/i+1)%mod)%mod+mod)%mod;
    }
    return (ans*anti4)%mod;
}

int main(){
    scanf("%lld%lld",&a,&b);
    n=max(a,b);
    mobius();
    printf("%lld\n",cal(a,b));
    return 0;
}


Problem Nine:[BZOJ2694LCM]:

sigma(lcm(i, j)),i ≤ a, j ≤ b,gcd(i,j)为free-squares.(所有质因子的次数<=1)

 

莫比乌斯逆变换,用逆变换的函数解原函数不易解决的问题,结合gcd函数的运算技巧,可以化简出结果。

和Problem Eight写法类似,代码略




Problem Nine:[BZOJ2694LCM]:

sigma(lcm(i, j)),i ≤ a, j ≤ b,gcd(i,j)为free-squares.(所有质因子的次数<=1)

 

你可能感兴趣的:(acm_数论)