数论基础练习赛-解题报告

45 篇文章 0 订阅
18 篇文章 0 订阅

A-prime Test Miller-Rabin 素数测试。不会搞,求不出最小质因子,数太大了。

B-Prime cuts

题意:给出n代表1-n之间的范围,m代表剪切素数的2*m或2*m-1的长度,从中间剪切,保证剩下的两端长度相同。

分析:素数筛选,找到在n范围素数的个数,即等到k。设剪切首的位置为pos,末尾位置为endpos。

根据题意:pos=n-1-endpos,

                  endpos-pos+1=2*m(2m-1)

pos=(k+2-2*m)/2;pos为整数,如果不能整除即取2*m-1,得pos=(k+2-2*m)/2+1;

B:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std ;
const int N=10005;
int prime[N],p[N];
int index=0;

void Getprime()
{
	for(int i=4;i<=N;i+=2){
		p[i]=1;
	}
	int n=(int)sqrt(N);
	for(int i=3;i<=n;i+=2){
		if(!p[i]){
			for(int j=i*i;j<=N;j+=2*i)
			p[j]=1;
		}
	}
	for(int i=1;i<=N;i++){
		if(!p[i]) prime[index++]=i;
	}
}

int main()
{
	int i,n,m,pos,endpos;
    Getprime();
    while(scanf("%d %d",&n,&m)!=EOF){
		bool flag=0;
		for(i=0;i<index;i++){
			if(n<prime[i]){
				flag=1;
				break;
			}
			else if(n==prime[i]){
				break;
			}
		}
		if(flag) i--;
		int tmp=i+2-2*m;
		if(tmp&1) pos=(tmp+1)/2,endpos=pos+2*m-1;
		else pos=tmp/2,endpos=pos+2*m;
		printf("%d %d:",n,m);
		if(pos<=0){
			for(int j=1;j<=i;j++){
				printf(" %d",prime[j]);
			}
			printf("\n\n");
		}
		else{
			for(int j=pos;j<endpos;j++){
			  printf(" %d",prime[j]);
		    }
		   printf("\n\n");
		}
	}
 	return 0 ;
}
C- Primary Arithmetic
题意:求两个数相加进位的次数。

模拟加法的水题!注意格式就行's'.

C:

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std ;
char st1[12],st2[12];
int add(char *st1,char *st2)  
{
	int i,i1,i2,tmp,carry;
	int ans=0;
	i1=strlen(st1)-1,i2=strlen(st2)-1;
	carry=0;
	for(;i1>=0&&i2>=0;--i1,--i2){
		tmp=st1[i1]-'0'+st2[i2]-'0'+carry;
		carry=tmp/10;
		if(carry>0) ans++;
	}
	while(i1>=0){
		tmp=st1[i1--]-'0'+carry;
		carry=tmp/10;
		if(carry>0) ans++;
	}
	while(i2>=0){
		tmp=st2[i2--]-'0'+carry;
		carry=tmp/10;
		if(carry>0) ans++;
	}
	return ans;
}
int main()
{   
	int ans;
    while(scanf("%s %s",&st1,&st2),st1[0]!='0'||st2[0]!='0'){
	   ans=add(st1,st2);
	   if(ans==0) puts("No carry operation.");
	   else if(ans==1)printf("%d carry operation.\n",ans);
	   else printf("%d carry operations.\n",ans);
    }
 	return 0 ;
}
E - Ones

题意:给出n不能被2或5整除的数,存在为位数全部1的数,输出它最短能被n整除的位数。

同余定理的大水题。

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std ;

int main()
{
	int n,i,sum;
    while(~scanf("%d",&n)){
		if(n%2==0||n%5==0) continue;
		sum=0;
		for(i=0;;i++){
		   sum=(sum*10+1)%n;
		   if(sum==0){
				printf("%d\n",i+1);
				break;
		   }
		}  
    }
 	return 0 ;
}
F-青蛙的约会

扩展欧几里得(线性同余方程)

#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long LL;
using namespace std ;

LL exgcd(LL a,LL b,LL &x,LL &y)
{
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	LL d=exgcd(b,a%b,x,y);
	LL t=x-a/b*y;
    x=y;
    y=t;
	return d;
}

int main()
{
	LL x1,x2,m,n,l;
	LL x,y,c,d;
    while(scanf("%lld %lld %lld %lld %lld",&x1,&x2,&m,&n,&l)!=EOF){
		c=x1-x2;
		d=exgcd(n-m,l,x,y);
		if(c%d){
			puts("Impossible");
			continue;
		}
	    l/=d,c/=d;
		LL Min=((x*c)%l+l)%l;
		printf("%lld\n",Min);
    }
 	return 0 ;
}

G-Relatives

题意:求前n个数与n互质的个数(欧拉函数)

离散数学有个欧拉函数公式:f(n)=n(1-1/p1)*(1-1/p2)*....(1-1/pn) 其中:n=(p1^q1)*(p2^q2)*...(pn^qn)  pn代表该数的一个质因子。

G:

#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long LL;
using namespace std ;

LL eular(LL x)
{
	LL ret=1;
	for(int i=2;i*i<=x;i++){
		if(x%i==0){
			ret*=i-1;
			x/=i;
			while(x%i==0){
				x/=i;
				ret*=i;
			}
		}
	}
	if(x>1) ret*=x-1;
	return ret;
}

int main()
{
	LL n;
    while(scanf("%lld",&n),n){
		printf("%lld\n",eular(n));
    }
 	return 0 ;
}


H-Prime Land
题意:由n=(p1^e1)*(p2^e2)*...(pn^en) 求n-1的质因子和相应的指数,给出是p1 e1 p2 e2....pn en,输出是n-1的数分解因子的形式,按因子从大到小排列。

分析:先求出n,再分解n-1,很基础的一道质因子分解题。

H:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std ;
struct node{
	int x,y;
}ans[500];

char st[1005];
int a[1005],e,cnt;

void read_num(char *s)
{
	char t[10];
	int p=0,len=strlen(s);
	cnt=0;
	s[len]=' ';
	for(int i=0;i<=len;i++){
		if(s[i]==' '){
			t[p]='\0';
			sscanf(t,"%d",&a[cnt++]);
			p=0;
		}
		else t[p++]=s[i];
	}
}

void get_ans(int n)
{
	e=0;
	for(int i=2;i*i<=n;i++){
		if(n%i==0){
			n/=i;
			ans[e].x=i;
			ans[e].y=1;
			while(n%i==0){
				n/=i;
				ans[e].y++;
			}
			e++;
		}
	}
	if(n>1) ans[e].x=n,ans[e++].y=1;
}

int main()
{
	int num;
    while(gets(st)){
		if(st[0]=='0') break;
		read_num(st);
		num=1;
		for(int i=0;i<cnt;i+=2){
			num*=(int)pow(a[i],a[i+1]);
		}
		get_ans(num-1);
		for(int i=e-1;i>=0;i--){
			printf(i==e-1?"%d %d":" %d %d",ans[i].x,ans[i].y);
		}
		puts("");
    }
 	return 0 ;
}

I-prime gap

题意:在所有的素数中,给出一个数,求在两个相邻素数中包含该数n的相邻长度。

分析:筛选素数,查找n在两个相邻素数的位置,输出他们的长度即差,如果n是素数,答案为零,因为没有存在连续相邻的数为素数。

I:

#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long LL;
using namespace std ;
const int N=2000000;
LL prime[N];
bool vis[N];
int index=0;

void Get_prime()
{
	for(int i=2;i<=N;i++){
		if(!vis[i]) prime[++index]=i;
		for(int j=1;j<=index&&prime[j]*i<=N;j++){
			vis[prime[j]*i]=1;
			if(i%prime[j]==0) break;
		}
	}
}

int main()
{
	LL n;
	Get_prime();
    while(scanf("%lld",&n),n){
		if(!vis[n]){
			puts("0");
			continue;
		}
		for(int i=1;i<=index;i++){
			if(n<prime[i]){
				printf("%lld\n",prime[i]-prime[i-1]);
				break;
			}
		}
    }
 	return 0 ;
}

J-prime path

题意:给出两个四位数的素数,从第一个素数变到第二个素数,且每次变的过程中也必须为素数,变一次只能修改一个数的位数,且首位不能修改为零。

分析:先筛选素数,然后进行BFS,按着位去搜就行了,还是很基础的BFS。

J:

#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;

const int N=10000;
const int a[4]={1,10,100,1000};
int visit[N],prime[N];

void Getprime()           
{
	int i,j;
	for(i=4;i<=N;i+=2){
	  prime[i]=1;
	}
	int n=(int)sqrt(N);
	for(i=3;i<=n;i+=2){
	 if(prime[i]) continue;
	for(j=i*i;j<=N;j+=2*i)
	  prime[j]=1;
	}
}

void prime_BFS(int k,int m)
{
	queue<int>Q;
	Q.push(k);  
	visit[k]=0;
    while(!Q.empty())
    {
        int s=Q.front();
        Q.pop();
		if(s==m){
		  printf("%d\n",visit[s]);
		  return ;
		}
        for(int i=0;i<4;i++)
        { 
         for(int j=0;j<10;j++)
          {
           int x=s/(a[i]*10);    
           int y=s%a[i];
           int num=x*a[i]*10+j*a[i]+y;
           if(num>1000&&visit[num]==-1&&!prime[num])  
            {   
		 	 Q.push(num);
			 visit[num]=visit[s]+1;
            }
          }
        }
	}
   puts("Impossible");
}

 int main()
{  
	 int n,k,m;
     Getprime();
     while(scanf("%d",&n)!=-1)
	   while(n--)
       {
         memset(visit,-1,sizeof(visit));
         scanf("%d %d",&k,&m); 
		 if(!prime[k]&&!prime[m])    
          prime_BFS(k,m);
         else puts("Impossible");
       }
       return 0;
}


          
         

总结:本次比赛题目还是很基础的,A题的个数还是不多,主要原因是读题的速度慢,理解能力差,对基础的算法虽了解但不牢固,平时还是要多训练阅读能力和写代码的能力。

两种线性筛选素数的模板和欧拉函数模板我都敲下来了,果断敲了四五遍,应该很实用的,部分由讲解。

欧拉函数与素数筛选。

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;    //本程序分别用两种方法求欧拉函数和素数筛选,并最后用一个函数 
const int N=1000000;   //实现素数筛选和欧拉函数值,算法效率都非常高,都很有技巧。 
                      //这些典型的算法很基础,在平时训练中要做到水到渠成! 
int prime[N],phi[N];
bool vis[N];

void Get_prime1()   //算法思想:先一次性筛选偶数,用每个素数一次性筛选含有它的因子的合数。 
{                  //此算法适宜:素数的标记。 
	int index=0;
	for(int i=4;i<=N;i+=2) vis[i]=1;
	int n=(int)sqrt(N);
	for(int i=3;i<=n;i+=2){
		if(vis[i]) continue;
		for(int j=i*i;j<=N;j+=2*i)
		vis[j]=1;
	}
	for(int i=2;i<=N;i++){
		if(!vis[i]) prime[++index]=i;
	}
}

void Get_prime2()   //与上述的思路一样,但算法更精简。 
{                  //算法适宜:存取素数。 
	int index=0;
	for(int i=2;i<=N;i++){
		if(!vis[i]) prime[++index]=i;
		for(int j=1;j<=index&&prime[j]*i<=N;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0) break;   //i含有一个素因子就“没必再用”i的倍数去筛选素数了,因为可以用 
		}                              //更小的素因子筛选了,保证不重复筛选一个合数了。 
	}
}


void Get_phi()//算法思路:根据n=(p1^q1)*(p2^q2)*..(pn^qn)===>phi[n]=n*(1-1/p1)*(1-1/p2)..(1-pn).
{                              //==>phi[n]=n*[(p1-1)/p1]*...[(pn-1)/pn] 
	phi[1]=0;                  //==>phi[n]=phi[n]/pi*(pi-1)  且  n%pi=0. 
	for(int i=2;i<=N;i++) phi[i]=i;
	for(int i=2;i<=N;i+=2) phi[i]/=2;   //偶数必含2的因子,根据公式n*(1-1/2)*..=(n/2)*.....。 
	for(int i=3;i<=N;i+=2) if(phi[i]==i){
		for(int j=i;j<=N;j+=i){          //素数i,搜索i的倍数,即含有该素因子的合数。 
			phi[j]=phi[j]/i*(i-1);      //见算法思路。 
		}
	}
}

int eular(int n)     //此算法思路还是很简单的,只适宜求单个欧拉函数值 
{
	int ret=1;
	for(int i=2;i*i<=n;i++){
		if(n%i==0){
			n/=i;
			ret*=i-1;
			while(n%i==0){
				n/=i;
				ret*=i;
			}
		}
	}
	if(n>1) ret*=n-1;
	return ret;
}

void prime_phi()   //此算法非常有技巧,把素数和欧拉函数结合起来,实现两种功能。 
{
	int index=0;
	for(int i=2;i<=N;i++){
		if(!vis[i]){
			prime[++index]=i;
			phi[i]=i-1;                   //素数与它前面n-1个都互质。 
		}
		for(int j=1;j<=index&&prime[j]*i<=N;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0){       //i含有一个素因子就“没必再用”i的倍数去筛选素数了,因为可以用 
				phi[i*prime[j]]=phi[i]*prime[j];  //更小素因子去筛选,它的欧拉函数值,正好多phi[i]个。 
				break;                            //phi[i]*(prime[j]-1)+phi[i]=phi[i]*prime[j];
			} 
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

void Test(int n)
{
	for(int i=1;i<=n;i++){
		printf("prime[%d]=%d phi[%d]=%d\n",i,prime[i],i,phi[i]);
	}
	printf("\n");
}

int main()
{
	int n;
	//Get_prime1();
	//Get_phi();
	//Get_prime2();
	prime_phi();
	while(scanf("%d",&n)!=EOF) Test(n);
	
	//eular(n);
	return 0;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值