[Kuangbin带你飞]专题十四 数论基础

A. Bi-shoe and Phi-shoe: 给出一个数列a,对于其中的每个元素,求出欧拉函数值不小于此元素的最小数。

题解:由欧拉函数的定义可以得到,对于一个数字x,欧拉函数值不小于x的最小数是不小于x的最小素数。因此先打出素数表,再二分地对于每一个元素找出不小于它的最小素数即可。

#include<bits/stdc++.h>
#define maxn 10050
#define maxm 2000050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int t,n,a,p,kase=0;
int pri[maxm];
bool flag[maxm];

void init(int x)
{
    for(int i=0;i<=x;i++)flag[i]=1;
    flag[0]=flag[1]=0;
    p=0;
    for(int i=2;i<=x;i++)
    {
        if(flag[i])
        {
            pri[p++]=i;
            for(int j=i*2;j<=x;j+=i)
                flag[j]=0;
        }
    }
}

int main()
{
    scanf("%d",&t);
    init(2e6);
    while(t--)
    {
        scanf("%d",&n);
        ll ans=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a);
            int tmp=upper_bound(pri,pri+p,a)-pri;
            ans+=pri[tmp];
        }
        printf("Case %d: %lld Xukha\n",++kase,ans);
    }
    return 0;
}

C. Aladdin and the Flying Carpet: 给定两个数a和b,求b到sqrt(a)中有多少个数是a的因数。

题解:有这样一个定理:如果一个数x的唯一分解式为(p1^e1)*(p2^e2)*……*(pn^en),那么x的因数数目就是(1+e1)*(1+e2)*……*(1+en)。显然1-sqrt(x)内的因数数目就是总的因数数目除以2。然后枚举1-b减去这个区间内的因子即可。

#include<bits/stdc++.h>
#define maxn 1000050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int t,num=0,p,kase=0;
ll a,b;
int pri[maxn];
bool flag[maxn];

void init(int x)
{
    for(int i=0;i<=x;i++)flag[i]=1;
    flag[0]=flag[1]=0;
    for(int i=2;i<=sqrt(x);i++)
    {
        if(flag[i])
        {
            for(int j=i*i;j<=x;j+=i)
                flag[j]=0;
        }
    }
    for(int i=2;i<=x;i++)
    {
        if(flag[i])
            pri[num++]=i;
    }
}

ll coun(ll x)
{
    ll cnt=x;
    ll cur=0,ans=1;
    int i=0;
    if(x==0)return 0;
    while(i<num&&pri[i]<=sqrt(1.0*cnt))
    {
        if(cnt%pri[i]==0)
        {
            p=0;
            while(cnt%pri[i]==0)
            {
                p++;
                cnt/=pri[i];
            }
            ans*=(1+p);
        }
        i++;
    }
    if(cnt>1)ans*=2;
    return ans;
}

int main()
{
    scanf("%d",&t);
    init(1000000);
    while(t--)
    {
        scanf("%lld%lld",&a,&b);
        ll ans;
        if(b>=sqrt(1.0*a))ans=0;
        else
        {
            ans=coun(a)/2;
            for(ll i=1;i<b;i++)
                if(a%i==0)ans--;
        }
        printf("Case %d: %lld\n",++kase,ans);
    }
    return 0;
}

D. Sigma Function: 定义一个函数如下图,给定n,问1-n中有多少个数的函数值为偶数。


题解:只要某一项为偶数,整个函数值就为偶数。所以可以先讨论奇数的情况。每一项可以表示为1+pi+p2^2+……+pi^ei,只有pi与ei同时为奇数时,这项的结果才会为偶数。也就是说对于唯一分解式中的pi^ei,有pi为偶或者pi奇ei偶的情况。

pi偶ei偶与pi奇ei偶的情况中,既然指数都为偶数,那么这就一定是完全平方数。而pi偶ei奇的情况中,一个数的偶素因子只可能为2,也就是说是2的奇数次方。提出一个2来,就可以看出这个数显然是某个完全平方数的2倍。

也就是说一个数如果不是完全平方数或者完全平方数的两倍,它的函数值就为偶数。

因此答案为n-sqrt(n)-sqrt(n/2)。

E. Leading and Tailing: 给定两个数n和k,求n^k的前3位和后3位数。

题解:后3位只要在快速幂中对1000取模即可。前三位:

F. Goldbach's Conjecture: 给定一个数n,问存在多少对素数(a,b)满足a+b=n。

题解:打出素数表来,枚举小于等于n/2的每个素数,看n和它之差是不是素数即可。

G. Harmonic Number(II): 给定n,求n/i(i from 1 to n)之和。

题解:先直接处理出1-sqrt(n)的值,对于后面的值可以利用它们求出,例如值为1的有(n/1-n/2)项,值为2的有(n/2-n/3)项,直至n/i等于n/i+1。这之中n/sqrt(n)被计算了两次,需要减去。

#include<bits/stdc++.h>
#define maxn 10000050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const ll mod = 1000;

int t,n,kase=0;

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        ll ans=0;
        for(ll i=1;i*i<=n;i++)
        {
            ans+=(n/i)*(i-n/(n/i+1));
            if(n/i!=i)
                ans+=i*(n/i-n/(i+1));
        }
        printf("Case %d: %lld\n",++kase,ans);
    }
    return 0;
}

H. Pairs Forming LCM: 给定正整数n,求有多少对(i,j)满足lcm(i,j)=n。

题解:对于正整数a和b,lcm(a,b)是a,b的所有素因子在其唯一分解式中较高次幂的乘积。先将n分解,对于每一项pi^ei,有两种情况:

若pi在a中的指数等于ei,那么其在b中的指数可以取0-ei,有(ei+1)种情况。若不等于ei,那么在b中的指数必为ei,a中指数可以取ei种情况。那么对于n的每个素因子都有2*ei+1种情况,将其全部相乘得到ans。考虑a和b的大小关系,除了a,b都为n的情况,都出现了两次,因此最终答案就是ans/2+1。

I. Harmonic Number: 给定n,求调和级数的前n项。

题解:这个似乎和数论没什么关系啊……?采用了分段打表的思想,每100项的结果存起来,每次只需枚举不足整百的部分即可。

#include<bits/stdc+++.h>
#define maxn 1000050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const ll mod = 1000;

int t,n,kase=0;
double ans[maxn];

void init()
{
    double tmp=0;
    for(int i=1;i<=100000000;i++)
    {
        tmp+=(1.0/i);
        if(i%100==0)
            ans[i/100]=tmp;
    }
}

int main()
{
    scanf("%d",&t);
    init();
    while(t--)
    {
        scanf("%d",&n);
        double tmp=ans[n/100];
        for(int i=n/100*100+1;i<=n;i++)
            tmp+=(1.0/i);
        printf("Case %d: %.10lf\n",++kase,tmp);
    }
    return 0;
}

J. Mysterious Bacteria: 给定整数x,寻找一个尽可能小的数使得x为这个数的幂,求这个幂指数。

题解:将x唯一分解,则ans=gcd(e1,e2,……,ek)。有一个坑点是x可能为负,此时结果的幂指数不能为偶数。

#include<bits/stdc++.h>
#define maxn 10000050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const ll mod = 1000;

int t,num=0,kase=0;
ll n;
int pri[maxn/10];
bool flag[maxn];


void init()
{
    memset(flag,0,sizeof(flag));
    flag[0]=flag[1]=1;
    for(int i=2;i<=sqrt(10000000);i++)
    {
        if(!flag[i])
        {
            for(int j=i*i;j<=10000000;j+=i)
                flag[j]=1;
        }
    }
    for(int i=2;i<=10000000;i++)
    {
        if(!flag[i])
            pri[num++]=i;
    }
}
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}

int cal(ll x)
{
    int cur,ans=0;
    ll tmp=x;
    for(int i=0;i<num&&pri[i]*pri[i]<=tmp;i++)
    {
        if(tmp%pri[i]==0)
        {
            cur=0;
            while(tmp%pri[i]==0)
            {
                cur++;
                tmp/=pri[i];
            }
            if(ans==0)ans=cur;
            else ans=gcd(ans,cur);
        }
    }
    if(tmp>1)ans=gcd(ans,1);
    return ans;
}


int main()
{
    scanf("%d",&t);
    init();
    while(t--)
    {
        scanf("%lld",&n);
        bool vis=0;
        if(n<0)
        {
            n=(-n);
            vis=1;
        }
        int ans=cal(n);
        if(vis&&ans%2==0)
            while(ans%2==0)ans/=2;
        printf("Case %d: %d\n",++kase,ans);
    }
    return 0;
}

K. Large Division: 200位大数对int取模

题解:同余定理,按位取模即可。

L. Fantasy of a Summation: 给出一个长度为n的数列,选k个可重复的数字有n^k种方法,求所有方案选出的元素之和。

题解:进行了n^k次加法,每个数被选中的概率都是k/n,因此答案为sum*k*n^(k-1)。

M. Help Hanzo: 问a-b中有多少个素数。b-a<=1e5。

题解:区间素数筛法模板。

#include<bits/stdc++.h>
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int t,cont,kase=0;
ll m,n,z,p[maxn];
bool flag[maxn],f[maxn];

void getPrime()
{
    z=0;
    memset(flag,true,sizeof(flag));
    for (int i=2;i<=100000;i++)
    {
        if (flag[i])
        {
            p[z++]=i;
            for (int j=i*i;j<=100000;j+=i)
                flag[j]=0;
        }
    }
}

int main()
{
    ll sz,k;
    getPrime();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld",&m,&n);
        cont=0;
        if(n<70000)
        {
            for(ll i=m;i<=n;i++)
                if(flag[i] && i!=1)cont++;
        }
        else
        {
            if(m<=2) m=2;
            sz=n-m;
            for(ll i=0;i<=sz;i++)
                f[i]=true;
            for(i=0;i<z&&p[i]*p[i]<=n;i++)
            {
                k=m/p[i];
                if(k*p[i]<m) k++;
                if(k<=1) ++k;
                while(k*p[i]<=n)
                {
                    f[k*p[i]-m]=false;
                    ++k;
                }
            }
            for(i=0;i<=sz;i++)
                if(f[i]) cont++;
        }
        printf("Case %d: %d\n",++kase,cont);
    }
    return 0;
}

N. Trailing Zeros(III): 给出一个数n,问是否存在x使得x的阶乘末尾恰好有n个0。若存在求出最小的x。

题解:一个数中含有多少个5,阶乘的末尾就会含有多少个0。用二分的方法找出这个最小的点。

#include<bits/stdc++.h>
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int t,n,kase=0;

int getnum(int x)
{
    int ans=0;
    while(x)
    {
        ans+=(x/5);
        x/=5;
    }
    return ans;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        int l=1,r=1000000000,ans;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(getnum(mid)>=n)
            {
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        printf("Case %d: ",++kase);
        if(getnum(ans)==n)printf("%d\n",ans);
        else printf("impossible\n");
    }
    return 0;
}

O. GCD-Extreme(II): 给定n,求每一对小于n的(i,j)的gcd之和。

题解:用b[n]表示1-n-1与n的gcd之和,即sum[n]=sum[n-1]+b[n]。

用a[i]表示gcd(n,x)=i的x的个数,显然b[n]=Sum(i*a[i])。所以只需求a[i]:gcd(n,x)=i,即gcd(n/i,x/i)=1。

因此只需要求出phi[i],即可知道与n/i互质的数目,这样就可以求解了。

#include<bits/stdc++.h>
#define maxn 4000050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,phi[maxn];
ll f[maxn],sum[maxn];

void init()
{
    memset(phi,0,sizeof(phi));
    phi[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!phi[i])
        {
            for(int j=i;j<maxn;j+=i)
            {
                if(!phi[j])phi[j]=j;
                phi[j]=phi[j]/i*(i-1);
            }
        }
    }
    memset(f,0,sizeof(f));
    for(int i=1;i<maxn;i++)
    {
        for(int j=i*2;j<maxn;j+=i)
            f[j]+=(i*phi[j/i]);
    }
    memset(sum,0,sizeof(sum));
    for(int i=2;i<maxn;i++)
        sum[i]=sum[i-1]+f[i];
}

int main()
{
    init();
    while(scanf("%d",&n)&&n)
        printf("%lld\n",sum[n]);
    return 0;
}

R. 青蛙的约会:有两只青蛙分别在长度为l的环的a,b位置,以x,y速度同向跳跃,问多长时间后可以相遇。

题解:

S. C Looooops: 对于循环for(int i=a,i!=b;i+=c),在模2^k下需要运行多少次才会跳出循环。

题解:如下图,解方程即可。

V. Maxmium GCD: 给出一个数列,求两两组合gcd的最大值。

题解:直接暴力就行,但是要注意读入的方法,可以用字符串读入。

W. Prime Time: 定义f(x)=x*x+x+41,求a-b的函数值中素数的比例。

题解:1e4的数据,预处理一下函数值的素性,然后对于每次查询维护前缀和即可。

Y. Super Powers: 输出1-2^64-1中同时是两个或两个以上数的幂的数。

题解:要想进行拆分,幂指数必须是合数。也就是幂指数只会是4-64中的合数。然后枚举底数进行判断,底数大约在1e5之内。为节省时间当幂达到2^64时就跳出循环,对于底数i最大的幂指数就是ceil(64/lgi*lg2)-1。由于存在略大于2^64的情况,此题需要使用unsigned long long。

#include<bits/ctdc++.h>
#define maxn 105
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef unsigned long long ull;

map<ull,int>m;
map<ull,int>::iterator it;
bool flag[maxn];
int p[maxn],num=0;

void init()
{
    memset(flag,0,sizeof(flag));
    for(int i=2;i<=100;i++)
    {
        if(!flag[i])
        {
            for(int j=i*2;j<=100;j+=i)
                flag[j]=1;
        }
    }
    for(int i=4;i<=100;i++)
    {
        if(flag[i])
            p[num++]=i;
    }
}

ull mi(ull a,int x)
{
    if(x==0)return 1;
    ull tmp=mi(a,x/2);
    ull ans=tmp*tmp;
    if(x%2==1)
        ans*=a;
    return ans;
}

int main()
{
    init();
    m[1]=INF;
    for(int i=2;i<(1<<16);i++)
    {
        double t=ceil(64.0/log(i)*log(2))-1;
        for(int j=0;p[j]<=t&&j<num;j++)
        {
            ull tmp=mi(i,p[j]);
            m[tmp]++;
        }
    }
    for(it=m.begin();it!=m.end();it++)
            cout<< it->first <<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/80020549