刚看完一点点数论,感慨万千,数论真的是够难啊!!!
1、最大公约。
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);}
2、最小共倍。
c=a*b/gcd(a,b) 或者a/gcd(a,b)*b注意区别,第一种有可能越界。
3、唯一分解定理。
对于任意一个大于1的自然数X2均可写为质数的积,而且这些素因子按大小排列之后,写法仅有一种方式。
例题:除法表达式。给出一个这样的除法表达式:X 1 / X 2 / X 3 / …/ X k ,其中X i 是正整数。除法
表达式应当按照从左到右的顺序求和,例如,表达式1/2/1/2的值为1/4。但可以在表达式中
嵌入括号以改变计算顺序,例如,表达式(1/2)/(1/2)的值为1。
输入X 1 , X 2 , …, X k ,判断是否可以通过添加括号,使表达式的值为整
数。K≤10000,X i ≤10 9 。
thinking:可以将这个表达式化为X1*(X2*X3*X4...)/X2
,想想为什么。
然后思路1、大数相乘,把上面的数字都乘起来除X2,判断是否为整数,可得正解,但有些麻烦。
2、把每个X都由唯一分解定理得到为许多有着各种指数的素数的乘积,然后判断X2的每个素数的指数,如果均小于分子相对应的指数,则可以得到整数。
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
bool vis[5000000];
int qq[555555];
int cc[555555];
int d[555555];
int gg[555555];
int gcd(int a,int b){
return b=0?a:gcd(b,a%b);
}
int get_prime(int n){ //素数打表
memset(vis,0,sizeof(vis));
int m=sqrt(n+0.5);
for(int i=2;i<=m;i++)if(!vis[i])
for(int j=i*i;j<=n;j+=i)vis[j]=1;
int k,i;
for(k=0,i=2;i<=n;i++){
if(!vis[i]){
qq[k++]=i;
// cout <<i << endl;
}
}
return k;
}
void get_weiyi(int n){ //唯一分解的素数指数打表
int j=0;
while(cc[n]!=1){
int x=0;
while(cc[n]%qq[j]==0){
cc[n]=cc[n]/qq[j]; //也就是能整除这个素数
x++;
}
d[j]+=x;
j++;
}
}
int main(){
freopen("in.txt","r",stdin);
int a,b,i,j,n,m;
memset(vis,0,sizeof(vis));
memset(qq,0,sizeof(qq));
memset(cc,0,sizeof(cc));
memset(d,0,sizeof(d));
cin >> n;
int maxn=0;
for(i=0;i<n;i++){
cin >> cc[i];
if(maxn<cc[i]) //找出最大的数,然后素数打表到这个数
maxn=cc[i];
}
int k=get_prime(maxn);
int x;
get_weiyi(0);
for(i=2;i<n;i++){ //然后得到他们的指数。
get_weiyi(i);
}
j=0;
while(cc[1]!=1){
int x=0;
while(cc[1]%qq[j]==0){
cc[1]=cc[1]/qq[j]; //也就是能整除这个素数
x++;
}
if(x<=d[j])cout << qq[j] <<"符合"<<endl;
else cout << qq[j] << "无法整除"<< endl;
j++;
}
//for(i=0;i<k;i++)
// cout << d[i] << "\t" << qq[i] << endl;
return 0;
}
3、还是由唯一分解定理,循环用X2除各个分子的与X2的最大公约数,最终如果X2为1则可以得到整数。
#include <iostream>
#include <math.h>
#include <string.h>
using namespace std;
bool vis[5555555];
int qq[555555];
int c1[666666];
int c2[666666];
int main(){
int n,i,j,k,m;
cin >> n;
m=sqrt(n+0.5);
for(i=2;i<=m;i++)if(!vis[i])
for(j=i*i;j<=n;j+=i)vis[j]=1;
for(j=0,i=2;i<=n;i++)
if(!vis[i])
qq[j++]=i;
for(i=0;i<j;i++)
cout << qq[i] << endl;
}
素数打表模版,返回的k是素数的数量。
int get_prime(int n){ //素数打表
memset(vis,0,sizeof(vis));
int m=sqrt(n+0.5);
for(int i=2;i<=m;i++)if(!vis[i])
for(int j=i*i;j<=n;j+=i)vis[j]=1;
int k,i;
for(k=0,i=2;i<=n;i++){
if(!vis[i]){
qq[k++]=i;
// cout <<i << endl;
}
}
return k;
}
唯一分解模版 d里面存的是qq素数中各个素数的指数
void get_weiyi(int n){ //唯一分解的素数指数打表
int j=0;
while(cc[n]!=1){
int x=0;
while(cc[n]%qq[j]==0){
cc[n]=cc[n]/qq[j]; //也就是能整除这个素数
x++;
}
d[j]+=x;
j++;
}
}
(筛选法+容斥)例子,找出N内的无平方因子,无平方因子是指对于n,任意k>1,n都无法整除k^2。
thinking:可以把i循环所有从2到根号n的数i^2的所有倍数都打上标记,意思把所有平方因子都选出来,剩下的就为非平方因子。
因为m=sqrt(n+0.5)。 m<=n的根号,所以筛选从2--m的所有数的平方的倍数即可,
代码:
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
bool vis[55555555];
int main(){ //.总是运行错误
//freopen("in.txt","r",stdin);
int a,b,i,j,n,m;
cin >> n;
m=sqrt(n+0.5);
for(i=2;i<=m;i++)if(!vis[i])
for(j=i*i;j<=n;j+=i*i)vis[j]=1; //每次增加的是i的平方的倍数
for(i=2;i<=n;i++)
if(!vis[i])cout <<i <<endl;
return 0;
}
扩展殴几里德算法:
用于线性方程ax+by=c
1、用gcd(a,b,d,x,y)找到x与y是否有一对整数解,若c/d为整数那么就有整数解。
且其一个解为x0=x*c/d,y0=y*c/d。通解为(x0+b/d,y0-a/d)。这时d=gcd(a,b)这个gcd是指最大公约数。
代码:
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
void gcd(int a,int b,int &d,int &x,int &y){ //扩展欧几里德算法
if(!b){d=a;x=1;y=0;}
else{
gcd(b,a%b,d,y,x);y-=x*(a/b);
}
}
int main(){ //.总是运行错误
int a,b,x,y,d,c;
cin >> a >> b >> c;
gcd(a,b,d,x,y); //得出的x,y的解为ax+by=gcd(a,b) 这里的gcd指最大公约数
//通解为(x0+k*(a/gcd(a,b)),y0-k*(b/gcd(a,b))),其中x0,y0为其的一个解
if(c%d==0){//只是其中一个解
cout << "x=" << c/d*x << "\ty=" << c/d*y << endl;
cout << "x的通解为=k*" << b/d <<"+"<< c/d*x << " y的通解=-k*" << a/d <<"+"<<c/d*y<< endl;
}else{
cout << "无整数解" << endl;
}
return 0;
}
取mod问题;
有公式:(a+b)%n=(a%n+b%n)%n.
(a-b)%n=((a%n-b%n)+n)%n 因为有可能a>b,但是其取mod后再减可能会为负数。
a*b%n=((a%n)*(b%n))%n 对于乘法可能左边的最终结果是int型,但右边最后一次%n可能会溢出,所以需要long long 来储存。
乘法取模模版:
用字符串储存,然后a=(a*10+b[i]-'0')%c;便可以做到
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
char qq[2000];
int main(){
int i,t,n,m,length;
cin >> qq >> m; //求大数qq%m的值。
length=strlen(qq);
for(i=0;i<length;i++){
t=t*10+qq[i]-'0'; //有可能最终取mod结果为int型,但是中间乘的时候溢出了,所以应该设置t为long long
t%=m;
}
cout << t << endl;
return 0;
}
幂取模与快幂运算模版:
可以像乘法一样,int ans=1。
for(i=0;i<k;i++)ans=ans*n%m. 表示有k个n相乘取mod.
这个方法的复杂度为O(N),有个快一点的办法是:
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
int pow_mod(int qq,int m,int n){ //注意越界问题就好了。
if(m==0)return 1;
int ans=pow_mod(qq,m/2,n);
ans=ans*ans%n; //这里需要注意*的是返回的值ans,而不是qq!!。
if(m%2==1)ans=ans*qq%n;
return ans;
}
int pow(int qq,int m){
if(m==0)return 1;
int ans=pow(qq,m/2);
ans=ans*ans;
if(m%2==1)ans=ans*qq;
return ans;
}
int main(){
int i,t,n,m,qq,length;
cin >> qq >> m >> n; //qq的m次方,对n取mod
cout << pow_mod(qq,m,n) << endl;
cout << pow(qq,m) << endl;
return 0;
}
例题Uva11582
题意是 给f(0)=0,f(1)=1,有f(i)=f(i-1)+f(i-2),给定a,b,c三个数,求f(a的b次方)对n取mod的值,
thinking:
无法就是巨大的斐波那契数,由于是取mod,所以必定有周期,注意大指数的所取的方式,然后每次算斐波那契数列的时候也取mod。
#define ll undesing long long 意思是定义long long为不带符号的ll 也就是其的范围比之前*2.也就是1*10^20,long long的范围为9*10^19
代码:
#include <iostream>
#include <string.h>
#include<cstdio>
const int N=1e6+100;
#define ll unsigned long long
using namespace std;
int mod,F[N];
ll n,a,b;
int find1(int n){
int i=3;
while(true){
F[i]=F[i-1]+F[i-2];
F[i]%=n;
if(F[i]==1&&F[i-1]==1){
return i-2;}
i++;
}
}
int pow_mod(ll a,ll b){
if(b==0)return 1;
long long ans=pow_mod(a,b/2);
ans=(ans%mod)*(ans%mod)%mod;
if(b%2==1)ans=ans*(a%mod)%mod;
return ans;
}
int main(){
freopen("in.txt","r",stdin);
memset(F,0,sizeof(F));
int T;
F[0]=0;
F[1]=1;
F[2]=1;
cin >> T;
int tt;
while(T--){
cin >> a >> b>> n;
mod=find1(n);
tt=pow_mod(a,b);
cout << F[tt] <<"\t"<< mod<<endl;
}
return 0;
}