[原创]数论个人模板

      作为一个ACMer不可能没有自己的模板,并不是别人的模板你都能看的懂,所以有自己的模板很重要,下面是我自己一边培训一边整的数论模板,内容很少都是一些数论基础模板(毕竟不是大牛),但是此博客会一直更新的,有新内容我会马上添加进来的,如果有错误,请指出,谢谢。

1数论基础

1)、数学公式

1^3+2^3+3^3+……+n^3=[n(n+1)/2]^2 

斐波那契数列通项公式为:

     f(n)=1/sqrt(5)(((1+sqrt(5))/2)^n+((1-sqrt(5))/2)^n)

若取前面k位的话:

     ans= -0.5*(log10(5.0))+m*log10((sqrt(5.0)+1.0)/2));

ans -= (int)ans; ans=pow(10.0,ans);ans *= 10^(k-1);取整

对于a^b取前几位:

LL Pow2(LL a , LL b) {

    double s = log10(double(a)) * b - LL(log10(double(a)) * b);

    s = pow(10,s);

    return s * 100;//这里是取a^b前三位

}

给一串不定长数字、空格字符,获取里面的数字。

C++

#include<sstream>

string s;

while (getchar() != '\n');

getline(cin,s);

int index = 0;

stringstream ss(s);

while (ss >> a[index])  ++index;

C

while (getchar() != '\n');

while ((buf = getchar()) != '\n')  

if (buf >= '0' && buf <= '9') {  

    ungetc(buf,stdin);  

     scanf("%d",&data[count ++]);  

2)、九余数定理

     给一个数取每一位上的数字相加直到相加等于小于10的数,678=6+7+8=21=2+1=3

     ans  =  n%9 ; if(ans==0 )ans=9;   

3) 、辗转相除法求最大公约数

int gcd(int x, int y){          return (!y)?x:gcd(y, x%y);     }

4)、威尔逊定理

,其中p为素数。

5)、裴蜀定理(或贝祖定理

abgcd(a,b) = d; 一定存在 ax+by = d;

6)、扩展欧几里得算法(求逆元)

   http://blog.csdn.net/zhjchengfeng5/article/details/7786595

   ax≡c(mod b).  等价于 ax+by=c,求最小x

   通解方程:

    x =x0 + (b/gcd)*t(t为>=0的整数)

     y = y0 -(a/gcd)*t (t为>=0的整数)

 

LL e_gcd(LL a,LL b,LL &x,LL &y){

    LL d=a;

    if(b==0){

        x=1;y=0;

    }

    else{

        d=e_gcd(b,a%b,y,x);

        y-=(a/b)*x;

    }

    return d;

}

LL cal(LL a,LL b,LL c){

    LL x,y;

    LL gcd=e_gcd(a,b,x,y);

    if(c%gcd!=0) return -1;

    LL t=b/gcd;

    return (x* c / gcd % t + t) % t;

}

7)、中国剩余定理(同余式)

a .、数组元素互质的情况

xa1(mod m1)

xa2(mod m2)…..

int Chinese_Remainder(int a[],int m[],int len)  // a[]存放余数  m[]存放两两互质的数

{

int i,d,x,y,Mi;

int ret=0;

int M=1;

for (i=0;i<len;i++) M*=m[i];//M是所有质数的乘积

for (i=0;i<len;i++)

{

Mi=M/m[i];//Mi是除m[i]外的所有质数的乘积

d= e_gcd (Mi,m[i],x,y);//Mi*x1(mod m[i])->xMim[i]的数论倒数

ret=(ret+x*Mi*a[i])%M;//X= ∑ a[i]*x*Mi

}

return (M+ret%M)%M;

}

m数组元素不一定互质的情况

LL Chinese_Remainder (LL n,LL m[],LL a[]){

    LL m1,r1,m2,r2,flag=0,i,d,x,y,c,t;

    m1=m[0],r1=a[0];

    flag=0;

    for(i=1;i<n;i++) {

        m2=m[i],r2=a[i];

        if(flag)continue;

        d = e_gcd (m1,m2,x,y);

        c=r2-r1;

        if(c%d) {//对于方程m1*x+m2*y=c,如果c不是d的倍数就无整数解

            flag=1;

            continue;

        }

        t=m2/d;//对于方程m1x+m2y=c=r2-r1,(x0,y0)是一组整数解,那么(x0+k*m2/d,y0-k*m1/d)也是一组整数解(k为任意整数)

        x=(c/d*x%t+t)%t;// x0=x*c/d,y0=x*c/d;保证x0是正数

        r1=m1*x+r1;//新求的r1就是前i组的解,Mi=m1*x+M(i-1)=r2-m2*y(m1为前im的最小公倍数);m2取余时,余数为r2//对以前的m取余时,Mi%m=m1*x%m+M(i-1)%m=M(i-1)%m=r

        m1=m1*m2/d;//m1为新的最小公倍数

    }

    if(flag)return -1;

    if(n==1&&r1==0)return m1;//结果不能为0

    return r1;

}

8)、筛法求素数、求因子个数

、运用筛法打印素数表

int prime[N]={0};

int flag[N]={0};

int Count[N]={0};

void PrimeTable(){

    int index=0;

    for(int i=2;i<=N;i++){

        if(!flag[i]){

            prime[index++]=i;

            for(int j=i*i; j<=N; j+=i) {

                flag[j]=1;

            }

        }

    }

}

、求X的因子个数,通过刚刚的素数表来计算

void Count_factor(int x){

    int i=0;

    int xx=x;

    Count[xx]=1;

    while(x>=prime[i]){

        int cc=0;

        while(x%prime[i]==0){

            x/=prime[i];

            cc++;

        }

        i++;

        Count[xx]=Count[xx]*(cc+1);

    }

}

c、求X因子的和(不加本身打表)

void init(){

    Count[0] = Count[1] = 0;

    for(int i = 2;i < N;i++){

        Count[i] = 1;

    }

    for(int i = 2;i <= N/2; i++){

        for(int j = i+i;j < N;j += i) {

            Count[j] += i;

        }

    }

}

9)、费马小定理(快速幂取模)

__int64 qpow(__int64 a,__int64 b,__int64 m){

    __int64 ans=1;

    while (b>0){

        if (b&1)  ans=(ans*a)%m;

        b >>= 1;

        a=(a*a)%m;

    }

    return ans;

}

10) 、矩阵快速幂

int n,m;//n矩阵大小,m矩阵个数

struct matrix{

    int a[1][1];

} origin,res;

//计算矩阵相乘

matrix multiply(matrix x,matrix y){

    matrix temp;

    memset(temp.a,0,sizeof(temp.a));

    for(int i = 0; i < n; i++){

        for(int j = 0; j < n ; j++){

            for(int k = 0; k < n; k++) {

                temp.a[i][j] += x.a[i][k] * y.a[k][j];

            }

        }

    }

    return temp;

}

void quick_matrix(int m){

    while(m){

        if(m&1)  res = multiply(res,origin);

        m = m >> 1;

        origin = multiply(origin,origin);

    }

}

11)、欧拉函数

//1--n-1所有与n互质的数的和为:Sum  = n*(eular(n)/2)

//暴力求n互质的个数

int eular(int n){

   int ret=1,i;

   for(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;

}

//内存允许情况下

LL prim[N], p[N];

LL index;

void find_prim()

{

    index = 0;

    for(LL i = 2; i <= N; i++){

        if(!p[i]){

            prim[index++] = i;

            for(LL j = i+i; j <= N; j+=i){

                p[j] = 1;

            }

        }

    }

}

LL enlur(LL a,LL index)

{

    LL s = 1;

    if(a == 0)   return 0;

    LL i = 0 ,tt = 0;

    while(prim[i] < a && i < index){

        tt = 0;

        if(a%prim[i] == 0){

            while(a%prim[i] == 0){

                a/=prim[i];

                tt++;

            }

        }

        s *= tt+1;

        i++;

    }

    if(a > 1)  s *= 1+1;

    return s;

}

//优化一般首选,打印欧拉函数表

__int64 phi[N]={0};

void phi_table(){

    phi[1]=1;

    for(int i=2; i<=N; i++){

        if(!phi[i]){

            for(int j=i; j<=N; j+=i){

                if(!phi[j]) phi[j]=j;

                phi[j]=phi[j]/i*(i-1);

            }

        }

    }

}

12)、反素数

//n以内因子最多的最小的那个X

LL bestnum , maxsum , n;//bestnum最小的,maxsum因子个数

LL prime[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};

void dfs(LL index,LL temp,LL sum){

    if(temp > n) return;

    //sum记录当前的因子个数,maxsum小就更新

    if(sum > maxsum){

        maxsum = sum;

        bestnum = temp;

    }

     //当因子个数相同时,取值最小的

    if(sum == maxsum && bestnum > temp) bestnum = temp;

    for(LL i=1;i<=63;i++){

        if(n < temp*prime[index]) break;

        temp *= prime[index];//累乘到当前数

        dfs(index+1,temp,sum*(i+1));

    }

}

调用// dfs(0,1,1);

//给一个数,求一个最小的正整数,使得它的因子个数为。

LL maxsum,n;

void dfs(int index,LL temp,int sum){

    if(sum > n) return;

    if(sum == n && maxsum > temp) maxsum = temp;

    for(int i=1;i<=63;i++){

        if(maxsum / prime[index] < temp || sum*(i+1) > n) break;

        temp *= prime[index];

        if(n % (sum*(i+1)) == 0)  dfs(index+1,temp,sum*(i+1));

    }

}

2、组合数学

1)、错排

选新娘问题,有NN<=20)对新人,其中有M个新郎找错新娘。问有多少种可能?

代码:

__int64 a[25];

void init(){

//这个是错排打表

    a[0] = 1; a[1]=0;  a[2]=1;

    for(i=3;i<=20;i++){

        a[i]=(i-1)*(a[i-1]+a[i-2]);

    }

}

__int64 Cmn(__int64 m, __int64 n){

     if( m==n || n == 0) return 1;

     LL s = 1 , t = 1;

     for(__int64 i = m; i > m - n; i--)   s *= i;

     for(__int64 i = 1; i <= n; i++)  t *= i;

     return s/t;

}

int main(){

    int tcase,m,n,i,j;

__int64 sum;

init();

scanf("%d",&tcase);

    while(tcase--){

        scanf("%d%d",&m,&n);

        sum=Cmn(m,n)*a[n];//Cmn是组合

        printf("%I64d\n",sum);

    }

    return 0;

}

2)、全排列

直接用c++stl库里面的东西!输出的结果直接就是字典序

sort(str,str+len);//这步是必须要的,先排序,len数组元素的长度

cout<<str<<" 所有全排列的结果为:"<<endl;

do{

for(int i=0;i<len;i++)  cout<<str[i]<<” “;

cout<<endl;

}while(next_permutation(str,str+len));

3)Cmn%p问题

//小数据情况下a[N][m]即可

void init(){

    int i,j;

    for(i=1;i<=N;i++){

        a[i][1]=i%p;

        a[i][0]=1;

    }

    for(i=2;i<=N;i++){

        for(j=1;j<=N;j++){

            a[i][j]=(a[i-1][j]+a[i-1][j-1])%p;//这个是公式

        }

    }

}

 

3、博弈论

1)、威佐夫博奕(Wythoff Game

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

int m,n;//两堆石子个数

while(cin>>m>>n)

{

   int ans = floor(abs(m - n)*(1.0+sqrt(5.0))/2.0);

if (ans == min(m,n)) cout<<0<<endl;

   else cout<<1<<endl;//   

}

2)、尼姆博奕(Nimm Game

有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

    int m,p[101];

    while(cin>>m,m)

    {

        int ans=0;

        for(int i = 0; i < m; i++)

        {

            cin >> p[i];

            ans ^= p[i];

        }

        if(ans) cout << "Win" << endl;

        else cout << "Not win" << endl;

        //如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0

        int s = 0;

        for(int i = 0; i < m; i++){

            if(p[i] > (p[i] ^ ans)  )s++;

        }

        cout<<s<<endl;

    }

posted on 2015-08-14 17:51  与我同在  阅读(232)  评论(1编辑  收藏  举报

导航