ACM必做50题的解题数论.docx
- 文档编号:7152778
- 上传时间:2023-01-21
- 格式:DOCX
- 页数:24
- 大小:22.29KB
ACM必做50题的解题数论.docx
《ACM必做50题的解题数论.docx》由会员分享,可在线阅读,更多相关《ACM必做50题的解题数论.docx(24页珍藏版)》请在冰豆网上搜索。
ACM必做50题的解题数论
poj1061青蛙的约会
这题用的是解线性同余方程的方法,之前没接触到过,搜索资料后看到一个人的博客里面讲的很好就拷贝过来了。
内容如下:
对于方程:
ax≡b(modm),a,b,m都是整数,求解x的值,这个就是线性同余方程。
符号说明:
mod表示:
取模运算
ax≡b(modm)表示:
(ax-b)modm=0,即同余
gcd(a,b)表示:
a和b的最大公约数
求解ax≡b(modn)的原理:
对于方程ax≡b(modn),存在ax+by=gcd(a,b),x,y是整数。
而ax≡b(modn)的解可以由x,y来堆砌。
具体做法如下:
第一个问题:
求解gcd(a,b)
定理一:
gcd(a,b)=gcd(b,amodb)
欧几里德算法
intEuclid(inta,intb)
{
if(b==0)
returna;
else
returnEuclid(b,mod(a,b));
}
附:
取模运算
intmod(inta,intb)
{
if(a>=0)
returna%b;
else
returna%b+b;
}
第二个问题:
求解ax+by=gcd(a,b)
定理二:
ax+by=gcd(a,b)=gcd(b,amodb)=b*x'+(amodb)*y'
=b*x'+(a-a/b*b)*y'
=a*y'+b*(x'-a/b*y')
=a*x+b*y
则:
x=y'
y=x'-a/b*y'
以上内容转自
由这个可以得出扩展的欧几里德算法:
intexGcd(inta,intb,int&x,int&y)
{
if(b==0)
{
x=1;
y=0;
returna;
}
intr=exGcd(b,a%b,x,y);
intt=x;
x=y;
y=t-a/b*y;
returnr;
}
代码:
#include
#include
#include
#include
usingnamespacestd;
__int64mm,nn,xx,yy,l;
__int64c,d,x,y;
__int64modd(__int64a,__int64b)
{
if(a>=0)
returna%b;
else
returna%b+b;
}
__int64exGcd(__int64a,__int64b)
{
if(b==0)
{
x=1;
y=0;
returna;
}
__int64r=exGcd(b,a%b);
__int64t=x;
x=y;
y=t-a/b*y;
returnr;
}
intmain()
{
scanf("%I64d%I64d%I64d%I64d%I64d",&xx,&yy,&mm,&nn,&l);
if(mm>nn)//分情况
{
d=exGcd(mm-nn,l);
c=yy-xx;
}
else
{
d=exGcd(nn-mm,l);
c=xx-yy;
}
if(c%d!
=0)
{
printf("Impossible\n");
return0;
}
l=l/d;
x=modd(x*c/d,l);///取模函数要注意
printf("%I64d\n",x);
system("pause");
return0;
}
POJ1142SmithNumber
题意:
寻找最接近而且大于给定的数字的SmithNumber
什么是SmithNumber?
用sum(int)表示一个int的各位的和,那一个数i如果是SmithNumber,则sum(i)=sigma(sum(Pj)),Pj表示i的第j个质因数。
例如4937775=3*5*5*65837,4+9+3+7+7+7+5=42,3+5+5+(6+5+8+3+7)=42,所以4937775是SmithNumber。
思路:
要寻找大于给定数字且最接近给定数字的SmithNumber,只要将给定数字不断的加1,判断它是否是SmithNumber就行了,如果是SmithNumber就立即输出。
但是如何判断是否是SmithNumber呢?
首先就是要对数字进行质因数分解。
质因数分解要保证因子都是质数。
这里先介绍一个如何判断一个数int是否是质数呢,如果对于这个数,i=2.....sqrt(int)都不是它的约数,那int就是一个质数。
所以我们可以将i初始化为2,然后不断递增i,看i是否是int的一个约数,如果是的话那i就是int的一个质因数(因为这个数是从2开始第一个可以整除int的数,它不可能是一个可以分解的合数,否则,它的约数在它之前就整除int),然后将int除以该质因数,重置i为2,重新对int判断它是否是质数。
这样最后剩下的int一定是一个质数,从而对int进行了质因数分解
然后就很简单的将数字各质因数的各位加起来,看和是否等于该数字的各位和,如果相等那它可能就是SmithNumber,为什么说只是可能呢,因为这个数可能是质数,但是质数不是SmithNumber。
#include
#include
intSum(intnumber)
{
intsum=0;
while(number!
=0)
{
sum+=number%10;
number/=10;
}
returnsum;
}
boolSmithNumber(intnumber)
{
inti=2;
inttemp=number;
intsumOfNumber=Sum(number);
intsum=0;
while(i<=(int)sqrt((double)number))
{
if(number%i==0)
{
sum+=Sum(i);
number/=i;
i=2;
}
else
{
++i;
}
//以上的代码做了无谓的计算,可用下面的代码,更新于20090904
//while(number%i==0)
//{
//sum+=sum(i);
//number/=i;
//}
//++i;
}
sum+=Sum(number);
if(sum==sumOfNumber&&number!
=temp)
{
returntrue;
}
else
{
returnfalse;
}
}
intmain()
{
while(true)
{
intnum;
scanf("%d",&num);
if(num==0)
{
break;
}
while(true)
{
if(SmithNumber(++num))
{
printf("%d\n",num);
break;
}
}
}
return0;
}
ACM——POJ2262(Goldbach'sConjecture)
题目地址:
题目思路:
对于任何一个偶数n,从x=1和y=n-1开始,看x、y是否是质数,不是则x+=2,y+=2
这题需要开很大的内存,我REn次,居然是因为数组开太小了,我这题耗时不是很理想,但我的耗内存
在我看到的中是最小的,所以看来OJ上的题只要能开内存的就尽量开。
估计我这题用栈耗时了。
#include
#include
#include
#include
#include
#include
usingnamespacestd;
//判断是否为质数的函数
intIsPrime(intx)
{
inti;
if(x<2)
return0;
for(i=2;i<=(int)(sqrt((double)x+0.5));i++)
if(x%i==0)
return0;
return1;
}
intmain()
{
//输入数,输入数/2向上延伸,输入数/2向下延伸,输入数/2
intm_Input,m_Num_Max,m_Num_Min,m_InputToTwo;
//总体输出
charm_Output[1000000];
memset(m_Output,0,1000000);
//标识m_Output的Pos
intm_Output_Pos=0;
//是否找到标识
boolb_Find;
//栈
stack
//临时数
intm_Value_Top;
//循环输入
while(scanf("%d",&m_Input)&&(m_Input!
=0))
{
b_Find=true;
//m_Input肯定是一个偶数
m_InputToTwo=m_Input/2;
//置值
m_Num_Max=m_Input-1;
m_Num_Min=1;
//寻找,直至都为素数或者找不到为止
while((!
IsPrime(m_Num_Max))||(!
IsPrime(m_Num_Min)))
{
//否则,前进&后退2格
m_Num_Max-=2;
m_Num_Min+=2;
//如果发生如下情况,则输出"Goldbach'sconjectureiswrong."
if((m_Num_Max
{
char*m_TempChar="Goldbach'sconjectureiswrong.\n";
strcat(m_Output,m_TempChar);
b_Find=false;
m_Output_Pos+=strlen(m_TempChar);
break;
}
}
//如果找到了
if(b_Find)
{
//将m_Input转换为字符串存入m_Output
while(m_Input!
=0)
{
m_Value_Top=m_Input%10;
m_Stack.push(m_Value_Top);
m_Input/=10;
}
while(!
m_Stack.empty())
{
m_Value_Top=m_Stack.top();
m_Output[m_Output_Pos++]=m_Value_Top+48;
m_Stack.pop();
}
//加入=
m_Output[m_Output_Pos++]='';
m_Output[m_Output_Pos++]='=';
m_Output[m_Output_Pos++]='';
//将m_Num_Min转换为字符串存入m_Output
while(m_Num_Min!
=0)
{
m_Value_Top=m_Num_Min%10;
m_Stack.push(m_Value_Top);
m_Num_Min/=10;
}
while(!
m_Stack.empty())
{
m_Value_Top=m_Stack.top();
m_Output[m_Output_Pos++]=m_Value_Top+48;
m_Stack.pop();
}
//加入+
m_Output[m_Output_Pos++]='';
m_Output[m_Output_Pos++]='+';
m_Output[m_Output_Pos++]='';
//将m_Num_Max转换为字符串存入m_Output
while(m_Num_Max!
=0)
{
m_Value_Top=m_Num_Max%10;
m_Stack.push(m_Value_Top);
m_Num_Max/=10;
}
while(!
m_Stack.empty())
{
m_Value_Top=m_Stack.top();
m_Output[m_Output_Pos++]=m_Value_Top+48;
m_Stack.pop();
}
//加入\n
m_Output[m_Output_Pos++]='\n';
}
}
//输出
printf("%s",m_Output);
system("pause");
return0;
}
POJ2407Relatives
这题从题意可以看出就是求比从1~n-1从有几个数和n没有公共因子,通常的算法很简单就能够想到,我开始也是按通常的做法写了一个,觉得
可能会TLE,果不其然,后来上网查了一下,知道了欧拉函数,这个就是求比n小的数中与n互质(也就是没有公共因子)的算法,看来还是那些经典的算法效率比较高,比纯用暴力试探高多了...
欧拉函数描述如下:
利用欧拉函数和它本身不同质因数的关系,用筛法计算出某个范围内所有数的欧拉函数值。
欧拉函数和它本身不同质因数的关系:
欧拉函数ψ(N)=N{∏p|N}(1-1/p)。
(P是数N的质因数)
如:
ψ(10)=10×(1-1/2)×(1-1/5)=4;
ψ(30)=30×(1-1/2)×(1-1/3)×(1-1/5)=8;
ψ(49)=49×(1-1/7)=42。
注意的是P是N的质因子,这里求质因子还是不能够用常规的判断这个数是不是质数,这样的话可能还会TLE,网上学到他们用的一个while()循环,感觉还挺巧的,学习了...
#include
#include
intenlerFun(intn)
{
intcount=n;
inti=2;
for(;i<=n;i++)
if(n%i==0)
{
count-=count/i;
while(n%i==0)
n/=i;
}
returncount;
}
intmain()
{
intinputVal=0;
intcount=0;
while(scanf("%d",&inputVal)&&inputVal!
=0)
{
count=enlerFun(inputVal);
printf("%d\n",count);
}
return0;
}
POJ1811PrimeTest
MillerRobin素性测试+Pollardrho寻找素因子
MillerRobin和Pollardrho的理论想非常强,细节这里就不说了,可以参考
算法导论第31章
#include
#include
#include
#defineMAX_L64//最长位数
#defineTIMES8//millerrobin素性测试的测试次数
#defineMAX_VAL(pow(2.0,60))//定义最大值
#defineCVAL200
usingnamespacestd;
//最小的素因子
__int64minFactor;
//
(1)计算a*bmodn,思路:
利用b的二进制表示进行拆分计算
//
(2)例如:
b=1011101那么a*bmodn=(a*1000000modn+a*10000modn+a*1000modn+a*100modn+a*1modn)modn
//(3)思路就是上面描述的那样,那么可以用从低位往高位遍历b,并用a来记录当前位为1的值,每次遇到b当前位为
//1就将结果值加上a并modn,然后a要乘以2
__int64multAndMod(__int64a,__int64b,__int64n)
{
a=a%n;
__int64res=0;
while(b)
{
//当前位为1
if(b&1)
{
//加上当前权位值
res+=a;
//相当于modn
if(res>=n)res-=n;
}
//乘以2,提高一位
a=a<<1;
//modn
if(a>=n)a-=n;
b=b>>1;
}
returnres;
}
//
(1)计算a^bmodn,思路:
和上面类似,也是利用b的二进制表示进行拆分计算
//
(2)例如:
b=1011101那么a^bmodn=[(a^1000000modn)*(a^10000modn)*(a^1000modn)*(a^100modn)*(a^1modn)]modn
//(3)思路就是上面描述的那样,那么可以用从低位往高位遍历b,并用a来记录当前位为1的值,每次遇到b当前位为
//1就将结果乘上a并modn,然后a要乘以a以提升一位
__int64modAndExp(__int64a,__int64b,__int64n)
{
a=a%n;
__int64res=1;
while(b>=1)
{
//遇到当前位为1,则让res*当前a并modn
if(b&1)
res=multAndMod(res,a,n);
//a*a以提升一位
a=multAndMod(a,a,n);
b=b>>1;
}
returnres;
}
//MillerRobin素性测试,true:
素数,flase:
合数
boolmillerRobin(__int64a,__int64n)
{
__int64u=0,cur=n-1;
intt=0;
boolfind1=false;
while(cur!
=0)
{
if(!
find1)
{
intpb=cur%2;
if(pb==0)t++;
elsefind1=true;
}
if(find1)
break;
cur=cur/2;
}
u=cur;
cur=modAndExp(a,u,n);
__int64now;
for(intp=1;p<=t;p++)
{
now=modAndExp(cur,2,n);
if(cur!
=1&&now==1&&cur!
=n-1)
{
//printf("%d%d\n",cur,now);
returnfalse;
}
cur=now;
}
if(cur!
=1)
{
//printf("a:
%I64du:
%I64dn:
%I64dval:
%I64d\n",a,u,n,start);
returnfalse;
}
//printf("a:
%I64du:
%I64dn:
%I64dval:
%I64d\n",a,u,n,start);
returntrue;
}
//利用MillerRobin对n进行n次素性测试
booltestPrime(inttimes,__int64n)
{
if(n==2)returntrue;
if(n%2==0)returnfalse;
__int64a;intt;
srand(time(NULL));
for(t=1;t<=times;t++)
{
a=rand()%(n-1)+1;
if(!
millerRobin(a,n))returnfalse;
}
returntrue;
}
__int64gcd(__int64a,__int64b)
{
if(b==0)return(a);
returngcd(b,a%b);
}
__int64PollardRho(__int64n,intc)
{
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ACM 50 解题 数论