[kuangbin带你飞]专题十四 数论基础——个人总结

最近做了数论基础的专题,没做完,剩了七道。以后再补吧!!!(标红即没做)

A - Bi-shoe and Phi-shoe

题意:一个人要去买竹子,长为n的竹子对应一个数f(n),f(n)的值为1到n-1之间的与n互质的个数。

之后给出m个数ai,对应的这个人买m根竹子长为bi,要满足f(bi)>=ai,并且SUM(ai)最小。输出

SUM(ai)。

思路:这里我们注意,虽然f(n)的值是1<=x

AC代码:

#include
#include
#define maxn 1000005
int data[maxn];
int main()
{
    int Case,count=1,i,j,old=2;
    data[1]=2;
    for(i=3;i<=maxn;i++)
    {
        int temp = sqrt(i);
        for(j=2;j<=temp;j++)
        if(i%j==0) break;
        if(j>temp)
        {
            for(j=old;j<= i-1;j++) data[j]=i;
            old=i;
        }
    }
    scanf("%d",&Case);
    while(Case--)
    {
        int n;
        long long ans=0;
        scanf("%d",&n);
        for(i=0;i

 

B - Prime Independence

C - Aladdin and the Flying Carpet 

题意:给出一个矩形的面积a,一个整数b,要求矩形的短边x>=b。问你还有这种矩形有几种可能。

实际上就是求sqrt(a)的因子中大于等于b的个数。

思路:唯一分解定理求解。唯一分解定理—一个数n可以唯一分解成n=p0^c0*p1^c1*...*pi^ci(pi为素数)

这题很奇怪,我写的代码一直T,我找的网上的代码就可以过。我甚至照着他的代码改也是T,很迷。这里给出

两份代码。希望有大佬能指出我T的原因。

AC代码:

#include 
#include 
#include 
#include 
#define ll long long
using namespace std;
int const MAX = 1e6 + 10;
int p[MAX];
bool u[MAX];
int num, cnt;
ll a, b, tmp;

void get_prime()  
{  
    memset(u, false, sizeof(u));
    for(int i = 2; i <= sqrt(MAX); i++)
        if(!u[i])
            for(int j = i * i; j <= MAX; j += i)
                u[j] = true;
    for(int i = 2; i <= MAX; i++)
        if(!u[i])
            p[cnt ++] = i;
}

void cal()
{
    for(int i = 0; i < cnt && p[i] <= sqrt(tmp); i++)
    {
        int cc = 0;
        while(tmp % p[i] == 0)
        {
            cc ++;
            tmp /= p[i];
        }
        num *= (cc + 1);

    }
    if(tmp > 1) //如果tmp不能被整分,说明还有一个素数是它的约数,此时cc=1
        num *= 2;
}

int main()
{
    int T;
    scanf("%d", &T);
    cnt = 0;
    get_prime();
    for(int ca = 1; ca <= T; ca++)
    {
        scanf("%lld %lld", &a, &b);
        if(a < b * b)
            printf("Case %d: 0\n", ca);
        else
        {
            num = 1;
            tmp = a;
            cal();
            num /= 2;
            for(int i = 1; i < b; i++)
                if(a % i == 0)
                    num --;
            printf("Case %d: %d\n", ca, num);
        }
    }
}

我的代码:

 

#include
#include
#define maxn 1000010
bool prim[maxn];
int su[maxn],cnt;
long long a,b,temp,ans;
void inti()
{
    int i,j;
    prim[1]=1;
    for(i=2;i<=maxn;i++)
    {
        if(prim[i]==0)
        {
            for(j=i+i;j<=maxn;j+=i) prim[j]=1;
        }
    }
    for(i=2;i<=maxn;i++)
    if(prim[i]==0) su[cnt++]=i;
}
void cal()
{
    int i;
    int sq=sqrt(temp);
    for(i=0;i1) ans*=2;
}
int main()
{
    inti();
    int Case,count=0;
    scanf("%d",&Case);
    //freopen("f.txt","w",stdout);
    for(count=1;count<=Case;count++)
    {
        int i;
        scanf("%lld%lld",&a,&b);
        if(a<=b*b) printf("Case %d: 0\n",count);
        else
        {
            ans=1; temp=a;
            cal();
            ans>>=1;
            for(i=1;i

D - Sigma Function

题意:其实看题目就很清楚题意了。就是给了一个公式f(x),输入一个整数n,要求你输出1到n中,f(n)是偶数的个数。

思路:只要公式中有一项为偶数,那么f(n)就是偶数。所以我们反过来求奇数的个数再做个减法求得偶数的个数会更

容易些。看公式就会发现这公式其实是k个等比数列和的积。则当pi==2时,这一项便是奇数。因为pi是素数,所以

若pi!=2,则pi肯定是奇数。所以当pi!=2&&ei为偶数时,这一项便是奇数。

所以f(n)为奇数的情况就只有三种

1.n=2^k

2.n=x^2(ei为偶数,所以指数都可以提出一个2,这样就变成某个数的平方)

3.n=2*x^2(因为2的指数可偶可奇,偶数的情况包括在第二种情况,而奇数我们则提出一个2,就变成一个平方数的两倍)

综上,我们就可以给出AC代码了

#include
#include
#define maxn 1000000
int main()
{
    int Case,count=0;
    scanf("%d",&Case);
    while(Case--)
    {
        long long s,ans=0,sq;
        scanf("%lld",&s);
        int i;
        sq=(long long)sqrt((double)s);
        for(i=1;i<=sq;i++)
        {
            ans++;
            if((long long)2*i*i<=s) ans++;
        }
        printf("Case %d: %lld\n",++count,s-ans);
    }
    return 0;
}

 

E - Leading and Trailing 

题意:给出两个整数n和k,求n^k的前三位和后三位。

思路:后三位很容易得到,主要是前三位应该如何求出。求前三位就要知道点东西了。一个数a我们可以写成a=10^x,

其中x是一个浮点数。所以,若有10^x=n^k的话,则令y=(x的小数部分),前三位便是100*pow(10.0,y)。

这样我们便可以给出AC代码

#include
#include
#include
using namespace std;
#define ll long long
int main()
{
    int Case,count=0;
    scanf("%d",&Case);
   // ofstream my;
   // my.open("temp.txt");
    while(Case--)
    {
        int n,k,i,first,last;
        scanf("%d%d",&n,&k);
        first=n; last=1;
        int temp=n,fk=k;
        while(fk)
        {
            if(fk%2) last=(last*temp)%1000;
            temp=((temp%1000)*(temp%1000))%1000;
            fk>>=1;
        }
        double a=k*log10((double)n)-(long long)(k*log10((double)n));
        a=pow(10.0,a);


        printf("Case %d: %d %03d\n",++count,(int)(a*100),last%1000);

    }
     //my.close();
    return 0;
}

F - Goldbach`s Conjecture 

题意:验证哥德巴赫猜想:n=a+b,其中n为大于2的偶数,a<=b,给出n问有几种方案

思路:这....打表加暴力= =

AC代码:

 

#include
#include
#define maxn 10000000
bool Isprim[maxn];
int su[670000];
int cnt; //664579
void inti()
{
    int i,j;
    int sq=sqrt(maxn);
    Isprim[1]=1;
    for(i=2;i<=sq;i++)
    {
        if(Isprim[i]==0)
        {
            for(j=i*2;j

 

G - Harmonic Number (II)     

题意:额...这个应该不用说了...题目都直接上代码了- -

思路:假如给了整数n,那么我们先求SUM(n/i),其中1<=i<=sqrt(n),之后我们再求出(n/x)=b(b<=sqrt)的个数,

然后都加起来就好了。不过要注意一点:如果(n/sqrt(n))==sqrt(n)的话,我们就要减去一个sqrt(n)

AC代码:

#include
#include
long long solve(long long s)
{
    long long ans=0;
    int sq=sqrt((double)s),i;
    for(i=1;i<=sq;i++) ans+=(s/i);
    //printf("%d %d\n",ans,sq);
    for(i=1;i<=sq;i++)
    {
        ans+=i*((s/i)-(s/(i+1)));
        //printf("%d\n",ans);
    }
    if(sq==(s/sq)) ans-=sq;
    return ans;
}
int main()
{
    int Case,count=0;
    scanf("%d",&Case);
    while(Case--)
    {
        long long s;
        scanf("%lld",&s);
        printf("Case %d: %lld\n",++count,solve(s));
    }
    return 0;
}

 

 

H - Pairs Forming LCM                     

题意:直接看题目代码就理解了- -

思路:由唯一分解定理:n=p0^c0*p1^c1*...*pi^ci,a=p0^d0*p1^d1*...*pi^di,b=p0^e0*p1^e1*...*pi^ei

若a b的最小公倍数是n,则c0=max(d0,e0),c1=max(d1,e1)..ci=max(di,ei)

则(a,b)共有(2*c0+1)(2*c1+1)...(2*ci+1)(+1是因为有这种情况:di==ei==ci)

最后的结果要除以2,因为(a,b)和(b,a)是一样的

AC代码:

#include
#include
const int maxn = 1e7+5;
int su[maxn/10],cnt;
bool prime[maxn];
void inti()
{
	for(int i=2;i1) data[cc++]=1;
		for(int i=0;i

I - Harmonic Number        

题意:给一个整数n,求式子1+1/2+1/3+...+1/n   

思路:这题数据量有10^8,首先想到的是打表,可是开10^8的double数组肯定会MLE,所以我们每隔100才记录一次

AC代码:

 

#include
#define maxn 1000005
double data[maxn];
int main()
{
    int i; double s=1.0;
    for(i=2; i<=100000000 ;i++)
    {
        s+=(1.0/i);
        if(i%100==0) data[i/100]=s;
    }
    int Case,count=0;
    scanf("%d",&Case);
    while(Case--)
    {
        int a,i;
        scanf("%d",&a);
        double ans=data[a/100];
        for(i=(a/100)*100+1;i<=a;i++)
        ans+=(1.0/i);
        printf("Case %d: %.9lf\n",++count,ans);
    }
    return 0;
}

 

J - Mysterious Bacteria       

题意:其实题目巴拉巴拉说什么细菌我也看得不是很懂,这时候我们就要看样例猜题意了。实际上很简单,就是给你一个数n,有a^b=n,让你求出最大的b(例如n=64,n=2^6=4^3,这时候就要输出6,因为6是这些幂中最大的)

思路:很自然的就想到唯一分解定理了,n=p0^e0*p1^e1*...*pi^e1,那么答案ans就是e0 e1...ei这些指数的GCD...不过这里有一个坑点,就是n可能小于0,这时候我们的指数就不能是偶数,所以如果n<0,那么我们得加一句while(ans%2==0)ans/=2;

AC代码:

#include
#include
const int maxn = (1<<16)+5;
bool prime[maxn];
int su[maxn],cnt=0;
void inti()
{
	for(int i=2; i1) ans=1;
		else
		{
			ans =he[0];
			//printf("%d",ans);
			for(int i=1;i


             

K - Large Division                      

题意:给你两个整数a和b,问你a是否能整除b

思路:这里注意一下,a的数据量非常大,是不可能直接用long long的...所以我很无耻的掏出了java- -另外,这里直接取abs(a),abs(b)就好了...

AC代码:

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Scanner;

public class Main{
	public static void main(String[] args){
		Scanner cin =new Scanner(System.in);
		int n = cin.nextInt();
		BigInteger ZERO = BigInteger.valueOf(0);
		for(int i=1;i<=n;i++){
			BigInteger a,b,c;
			a=cin.nextBigInteger(); b=cin.nextBigInteger();
			a=a.abs(); b=b.abs();
			c=a.mod(b);
			System.out.print("Case "+i+": ");
			if(c.equals(ZERO)) System.out.println("divisible");
			else System.out.println("not divisible");
		}
	}
}

另外,C做大数实在是太麻烦了...建议看看Java...对于大数,Java不说秒出也差不多了

L - Fantasy of a Summation 

题意:自己看题目给的代码吧- -

思路:本蒟蒻用的是看样例猜公式法- -公式:ans = k*n^(k-1)*SUM(a[i]),运算的时候注意取模

AC代码:

#include
int main()
{
	//freopen("f.txt","w",stdout);
	int cases,count=0; scanf("%d",&cases);
	while(cases--)
	{
		int n,k,MOD;
		int ans = 0;
		scanf("%d%d%d",&n,&k,&MOD);
		for(int i=0;i

M - Help Hanzo                

题意:题目描述只需要看最后一段就好,就是让你求出整数a到b之间素数的个数

思路:第一反应当然是筛素数啊...不过数据量太大,直接求肯定会T。所以我们分两种情况来求。首先我们先素数打表,当然肯定不会直接打2^31,我打到2*10^5。如果给的数a小于这个范围,那么直接遍历素数数组,如果大于这个范围,那么我们就用类似筛素数的方法来做。

AC代码:

#include
#define maxn 200005
int su[maxn],cnt;
bool prime[maxn];
void inti()
{
	int i,j;
	prime[1]=1;
	for(i=2;i

看了代码,可能有人会问,会不会有种情况是有一个素数n>2*10^5,可是a<=k*n<=b,这种情况不是漏了?但事实上不会,因为注意到2*10^5是大于2^16的,所以在这之前这种情况就已经被筛掉了。

N - Trailing Zeroes (III) 

题意:给出一个整数Q,问你是否存在一个整数N,使得N!后恰好有Q个0,存在输出N,不存在输出impossible

思路:其实写几个阶乘看看就知道了。就是看这个数有几个5。不过这里我们得需要二分去找这个数。

AC代码:

#include
int main()
{
    int Case,count=0;
    scanf("%d",&Case);
    while(Case--)
    {
        int n; scanf("%d",&n);
        int l=4*n,r=5*n,flag=0;
        int mid;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            int mc=0,temp=mid;
            while(temp) mc+=temp/5,temp/=5;
            if(mcn) r=mid-1;
            else {flag=mid;break;}
        }
        printf("Case %d: ",++count);
        if(flag) printf("%d\n",flag-flag%5);
        else printf("impossible\n");
    }
    return 0;
}

O - GCD - Extreme (II)                            

P - Code Feat                        

Q - Emoogle Grid                        

R - 青蛙的约会  

题意:中文的就不用说了。

思路:我们照着题目要求推一下就会发现这其实就是裸的扩欧。  (x+ans*m)=k1*L+a,(y+ans*n)=k2*L+a

            ->(x-y)=L(k1-k2)+ans*(n-m)。不过这里注意,扩欧里是没有负数的。

AC代码:

#include
#define ll long long
ll exGcd(ll a, ll b, ll &x, ll &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll r=exGcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return r;
}
int main()
{
    ll x,y,m,n,L;
    scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L);
    ll a,b,ans,un;
    a = x-y; b = n-m;
    if(b<0) a=-a,b=-b;
    ll c=exGcd(L,b,un,ans);
    if(a%c!=0) printf("Impossible\n");
    else printf("%lld\n",(((a/c)*ans)%L+L)%L);
    return 0;
}

S - C Looooops 

T - Death to Binary?                   

U - Primes       

题意:问整数n是不是素数,题目给了1,2不是素数

思路:筛呗!

AC代码:

#include
#include
#define maxn 16005
bool primes[maxn];
int main()
{
    int i,j;
    primes[1]=0;
    primes[2]=0;
    for(i=3; i<=maxn; i++)
    {
        int temp=sqrt((double)i);
        for(j=2; j<=temp; j++)
        if(i%j==0) break;
        primes[i]=(j>temp);
    }
    int n,count=0;
    while(scanf("%d",&n) && n>0)
    {
        printf("%d: ",++count);
        if(primes[n]) printf("yes\n");
        else printf("no\n");
    }
    return 0;
}

                                    

V - Maximum GCD  

题意:给出多个整数,求这些整数之间的最大GCD。  

思路:这题就输入烦了点,暴力就好

AC代码:

#include
int Gcd(int a, int b)
{
    return a%b?Gcd(b,a%b):b;
}
int Max(int a, int b)
{
    if(a>b) return a;
    return b;
}
int main()
{
    int Case;
    scanf("%d",&Case);
    while(getchar()!='\n');
    while(Case--)
    {
        int data[1005]={0},count=0;
        char c;
        int flag=0;
        while((c=getchar())!='\n')
        {
            if(c==' ') {flag=1;continue;}
            if(flag) count++,flag=0;
            data[count]=data[count]*10+(c-'0');
        }
        int i,j,MMax=0;
        for(i=0;i

W - Prime Time

题意:给了一个函数f(n),输入两个整数a,b,求a,b之间的f(x),问素数的比例。例:a=0,b=39,f(0),f(1)...(f39)这40个数素数的比例。

思路:筛素数再打表

AC代码:

#include
#include
bool prime(int n)
{
    n=n*n+n+41;
    int sq=sqrt(n),i;
    for(i=2;i<=sq;i++)
    if(n%i==0) break;
    return (i>sq);
}
int main()
{
    //freopen("f.txt","w",stdout);
    bool ans[10005];
    int i;
    for(i=0;i<10005;i++)
    ans[i]=prime(i);
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        int count=0;
        for(i=a;i<=b;i++)
        count+=ans[i];
        //printf("%d %d\n",count,(b-a+1));
        printf("%.2lf\n",100*((double)count/(double)(b-a+1))+0.000001);
    }
    return 0;
}

 

 

 

 

X - The equation 

题意:让你求ax+by+c=0的解,其中解应满足x1<=x<=x2,y1<=y<=y2

思路:看到这式子很自然的就会想到扩欧吧。扩欧可以求一组特解x0,y0。则通解x=x0+k*b/Gcd(a,b),y=y0-k*a/Gcd(a,b)。然后解不等式就好了。这里有三个点要注意:一是a,b,c中存在负数的情况,二是a,b,c中存在为0的情况,三是最后求解k的时候需要注意向上或向下取整。

AC代码:

#include
#include
#include
#include
#include
using namespace std;
long long exGcd(long long a,long long b,long long &x,long long &y)
{
	if(!b){
		x=1; y=0; return a;
	}
	long long r=exGcd(b,a%b,x,y);
	long long t=x;
	x=y;
	y=t-(a/b)*y;
	return r;
}
int main(){
	long long a,b,c,x1,x2,y1,y2;
	scanf("%lld%lld%lld",&a,&b,&c);
	scanf("%lld%lld",&x1,&x2);
	scanf("%lld%lld",&y1,&y2);
	long long ans = 0; c= (-c);

	if(c<0) c=(-c),a=(-a),b=(-b);
	if(a<0) swap(x1,x2),x1=(-x1),x2=(-x2),a=(-a);
	if(b<0) swap(y1,y2),y1=(-y1),y2=(-y2),b=(-b);
	if(!a||!b)
	{
		if(!a&&!b){
			if(!c) printf("%lld\n",(x2-x1+1)*(y2-y1+1));
			else printf("0\n");
		}
		else if(a==0){
			long long temp=c/b;
			if(c%b==0&&temp>=y1&&temp<=y2) printf("%lld\n",(x2-x1+1));
			else printf("0\n");
		}
		else if(b==0){
			long long temp=c/a;
			if(c%a==0&&temp>=x1&&temp<=x2) printf("%lld\n",(y2-y1+1));
			else printf("0\n");
		}
		return 0;
	}

	long long hx,hy; long long myGcd;
	myGcd = exGcd(a,b,hx,hy);
	if(c%myGcd!=0) printf("0\n");
	else
	{
	    long long cheng = c/myGcd;
		double x = b/myGcd;
		double y = a/myGcd;
		hx*=cheng; hy*=cheng;

		long long r=min(floor((x2-hx)/x),floor((hy-y1)/y));
		long long l=max(ceil ((x1-hx)/x),ceil ((hy-y2)/y));
		if(r>=l) ans=r-l+1;
		else ans=0;
		printf("%lld\n",ans);
	}
	return 0;
}

 

Y - Farey Sequence  

题意:给一个整数n,问x(2<=x<=n)与(1,2....x-1)互质的个数

思路:裸的欧拉函数

AC代码:

#include
#include
const int maxn = 1e6+10;
int ans[maxn];
void inti()
{
    int i,j;
    ans[1]=1;
    for(i=2;i


                      

Z - The Super Powers 

题意:emmm....突然不知道该怎么表述,举个例子吧...输出一组数,64是这组数中的一员,因为64可表示为2^6或者4^3

思路:很显然,只要某一个数的指数可分解就可以输出了...则显然的指数最小为4,那么我们可以用一个循环遍历2到2^16就好了。这里我们用了set,因为set去重而且是排好序的...

AC代码:

 

#include
#include
#include
#include
using namespace std;
#define ll unsigned long long
#define Max 18446744073709551614
bool prime[70];
int Count,he[70],cnt;
set  data;
void inti()
{
    int i,j;
    for(i=2;i<65;i++)
    {
        if(prime[i])
        {he[cnt++]=i;continue;}
        for(j=i+i;j<65;j+=i)
                prime[j]=1;
    }
}
int main()
{
    //freopen("f.txt","w",stdout);
    inti();
    data.insert(1);
    ll ha= 1<<16;
    for(ll i=2;i::iterator i=data.begin();i!=data.end();i++)
    {
        printf("%llu\n",*i);
    }
    return 0;
}

 

 

 

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