ACM Steps_Chapter Seven_Section2

来源:互联网 发布:经传软件源代码 编辑:程序博客网 时间:2024/05/21 10:15

The Euler function

/*定义:    对于正整数n,φ(n)是小于或等于n的正整数中,与n互质的数的数目;                例如: φ(8) = 4, 因为1,3,5,7均和8互质。性质:  1.    若p是质数,φ(p)= p-1.               2.    若n是质数p的k次幂,φ(n)= (p-1)p^(k-1)                           因为除了p的倍数都与n互质               3.    欧拉函数是积性函数,若m,n互质,φ(mn)= φ(m)φ(n)               根据这3条性质我们就可以退出一个整数的欧拉函数的公式,因为一个数总可以一些质数的乘积的形式。               E(k) = (p1-1)(p2-1)…(pi-1)*(p1^(a1-1))(p2^(a2-1))…(pi^(ai-1))                        = k*(p1-1)(p2-1)…(pi-1)/(p1*p2*…pi)                        = k*(1-1/p1)*(1-1/p2)…(1-1/pk)在程序中利用欧拉函数如下性质,可以快速求出欧拉函数的值(a为N的质因素) 若(N%a==0 && (N/a)%a==0) 则有:E(N)=E(N/a)*a;          若(N%a==0 && (N/a)%a!=0) 则有:E(N)=E(N/a)*(a-1);以下是2种求欧拉函数的算法 1 void init() 2 { 3     __int64 i,j; 4     e[1] = 1; 5     for(i=2;i<=N;i++) 6         if(!e[i]) 7         {              8             for(j=i; j<=N; j+=i) 9             {    10                 if (!e[j])11                     e[j] = j;12                 e[j] = e[j] / i * (i-1);13             }    14         }15 }利用素数筛选:void init(){    __int64 i, j;        p[0] = 1; //记录素数个数    p[1] = 2;    for (i=3; i<N; i+=2)    {        if (hash[i])            continue;        p[++p[0]] = i;        for (j=i*i; j<N; j+=i)            hash[j] = true;    } //筛素数        e[1] = 1;    for (i=1; i<=p[0]; i++)        e[p[i]] = p[i] - 1; //初始化素数的phi    for (i=2; i<N; i++)    {        if(!e[i])        {            for (j=1; j<=p[0]; j++)                if (i % p[j]==0)                {                    if (i / p[j] % p[j])                        e[i] = e[i / p[j]] * e[p[j]];                    else                        e[i] = e[i / p[j] ]* p[j];                    break;                } // 利用上述性质求解        }            }    return ;}明显第一种的编程复杂度要低很多所以,一般情况下(N不是很大),采用第一种即可;*//*该题刚开始用最原始的方法,先筛出质数,再利用X=( p1-1 )*( p2-1 )*....*( pn-1 )*( p1^( q1-1 ) )*( p2^( q2-1 ) )*...*( pn^( qn-1 ) ),我就测试了一下3--3000000发现要好长的时间我就知道这种方法一定会超时;后来看了解题报告,才知道有如下的线性筛选法( 我个人刚开始不太喜欢线性法,因为我与其比较过其他的筛选法,发现并没有那么快,并且还要开那么大的数组,后来发现线性筛选法可以与好多的题目的结合处理特别好,现在也开始钟情它了 );在程序中利用欧拉函数如下性质,可以快速求出欧拉函数的值(a为N的质因素)(1) 若(N%a==0 && (N/a)%a==0) 则有:E(N)=E(N/a)*a;(2) 若(N%a==0 && (N/a)%a!=0) 则有:E(N)=E(N/a)*(a-1);*/#include<stdio.h>#include<stdlib.h>int  num[3000024],prime[220000];bool isprime[3000024]={0};void eular( ){   int count=0;   __int64 k;   for( int i=2; i<=3000000; i++ )   {       if( !isprime[i] )       {           prime[++count]=i;           num[i]=i-1;           }        for( int j=1; j<=count&&( (k=prime[j]*i)<=3000000 );j++  )       {            isprime[k]=1;            if( i%prime[j]==0 )            {                num[k]=num[i]*prime[j];                }             else num[k]=num[i]*( prime[j]-1 );           }       }      }int main( ){    int n,m;    eular();    while( scanf( "%d%d",&n,&m )!=EOF )    {       __int64 ans=0;       for( int i=n; i<=m; i++ )          ans+=num[i];        printf( "%I64d\n",ans );           }    return 0;    }

GCD Again

/*欧拉函数题目大意:给定一个N,求出所有满足条件GCD(M,N) > 1的M(0<M<N)的个数算法分析求出小于N且与N互质的数的个数,结果为:N-1-euler(N)。单独求欧拉函数的模版:int euler(int x) { int i,res = x; for(i = 2;i < (int)sqrt(x*1.0) + 1;i++)  if(x % i == 0)  {   res = res / i * (i-1);   while(x % i == 0) //保证i一定是素数    x /= i;  } if(x > 1)  res = res/x *(x-1); return  res; } */#include<iostream>#include<cmath>using namespace std;#define maxn 100000000int euler(int x) { int i,res = x; for(i = 2;i < (int)sqrt(x*1.0) + 1;i++)  if(x % i == 0)  {   res = res / i * (i-1);   while(x % i == 0) //保证i一定是素数    x /= i;  } if(x > 1)  res = res/x *(x-1); return  res; } int main(){ int n; while(scanf("%d",&n) && n)  printf("%d\n",n-1-euler(n)); return 0;}

A Simple Math Problem

//矩阵快速幂/*If x < 10 f(x) = x.If x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10);|f(10) |      |a0 a1 a2 ...a8 a9|   |f(9)|| f(9) |      | 1  0  0 ... 0  0|   |f(8)|| .....|  =   |.. ... ... ... ..|   | .. || f(2) |      | 0  0  0 ... 0  0|   |f(1)|| f(1) |      | 0  0  0 ... 1  0|   |f(0)|另A举证为10*10的举证,如上图。可以推出:(f(n),f(n-1),...,f(n-9))^(-1) = A^(n-9)*(f(9),f(8),...,f(0))^(-1)*/#include<iostream>using namespace std;__int64 k,m;struct mat{    int mar[10][10];}a,b,tmp;mat matrixmul(mat a,mat b){    int i,j,k;    for(i = 0;i < 10;i++)        for(j = 0;j < 10;j++)        {            tmp.mar[i][j] = 0;            for(k = 0;k < 10;k++)                tmp.mar[i][j] += (a.mar[i][k] * b.mar[k][j]) % m;            tmp.mar[i][j] %= m;        }    return tmp;}void matrix_binary(){    while(k)    {        if(k & 1)            b = matrixmul(b,a);        a = matrixmul(a,a);        k = k >> 1;    }}int main(){    int i;    while (scanf("%I64d%I64d",&k,&m) != EOF)    {        memset(a.mar,0,sizeof(a.mar));        for(i = 1;i < 10;i++)            a.mar[i][i-1] = 1;        memset(b.mar,0,sizeof(b.mar));        for(i = 0;i < 10;i++)            b.mar[i][i] = 1;        for(i = 0;i < 10;i++)            scanf("%d",&a.mar[0][i]);        if(k < 10)        {            printf("%d\n", k % m);            continue;        }        k -= 9;        matrix_binary();        int res = 0;        for(i = 0;i < 10;i++)            res += (b.mar[0][i] * (9-i)) % m;        printf("%d\n",res%m);    }    return 0;}

Hello Kiki

/*hdu 3579 Hello Kiki 中国剩余定理(不互质的情况)对互质的情况,处理起来比较方便,可以直接套模板本题给出不互质的模线性方程组,求出满足方程的最小正整数解方案:对于不互质的模线性方程组,可以进行方程组合并,求出合并后的方程的解,这样就可以很快地推出方程的最终解。两个方程合并的一种方法:x = c1 (mod b1)x = c2(mod b2) 此时b1,b2不必互质的。显然可以得到x = k1 * b1 + c1   x = k2* b2 + c2,两个方程合并一下就可以得到:k1 * b1 = c2 - c1 (mod b2),这样可以设g=gcd(b1,b2),于是就有b1/g*k1-b2/g*k2=(c2-c1)/g,显然判断(c2-c1)/g是否为整数就能判断是否存在解,这样在经过类似的变换就能得到k1 = K (mod (b2/g)),最后得到x = K*b1 + c1 (mod (b1 * b2/g))。对于题目所给正整数的要求,只有一种反例,就是结果输出为0的情况,这个可以特殊考虑,只需要考虑所有数的最小公倍数即可。*/#include <iostream>   #include <cstdio>   #include <cstdlib>   #include <cstring>   using namespace std;  __int64 x, y, t;  __int64 egcd(__int64 a, __int64 b)   {      if (b==0)         {          x=1;                y=0;             return a;         }      else       {          __int64 e=egcd(b,a % b);           t=x;           x=y;          y=t-a/b*y;             return e;         }  }  __int64 gcd(__int64 x, __int64 y)  {      if (!x || !y)          return x > y ? x : y;      for (__int64 t; t = x % y; x = y, y = t);      return y;  }  __int64 mm[10],rr[10];  int main()   {  int T,Case,N;    __int64 m1,m2,r1,r2,d,c,t;      bool flag;         scanf ("%d",&T);      for(Case = 1; Case <= T; Case++)          {          scanf ("%d",&N);          flag=0;                    for (__int64 i=0;i<N;i++)          {              scanf ("%I64d",&mm[i]);          }          for (__int64 i=0;i<N;i++)          {              scanf ("%I64d",&rr[i]);          }          m1=mm[0];          r1=rr[0];            for (__int64 i = 0; i < N - 1; i++)              {              m2=mm[i+1];              r2=rr[i+1];                 if (flag)                   continue;                 d = egcd(m1, m2);                 c = r2 - r1;               if (c % d)                    {                         flag = 1;                      continue;                 }              t=m2/d;                  x=(c/d*x%t+t)%t;                 r1=m1*x+r1;                  m1=m1*m2/d;              }          if (flag)              printf ("Case %d: -1\n",Case);          else          {              if (r1==0&&N>1)              {                  r1=mm[0];                  __int64 ans=1;                  for (int i=1;i<N;i++)                      r1=gcd(mm[i],r1);                  for (int i=0;i<N;i++)                      ans*=mm[i];                  r1=ans/r1;              }              if (r1==0&&N==1)                  r1=mm[0];              printf ("Case %d: %I64d\n",Case,r1);          }      }      return 0;  }  

Description has only two Sentences

/*(x^k-1)*(y/(x-1))=0(mod a0),再将(y/(x-1))和a0先消掉公因数,就是要求最小k满足x^k=1(mod m)k^euler(m)=1(modm),找euler(m)的因数中能满足k^x=1(mod m)的最小x(快速幂)循环节:a^b=a^(b+t)  (mod c)  ->  a^t=1(mod c) -> 满足a^x=1(mod c) 则 x=t*k;(抽屉原理)euler:     a^euler=1(mod c)*/#include<iostream>  #include<cstdio>  #include<cmath>  #include<algorithm>  using namespace std;  int d[1000000];  typedef long long  ll;  ll euler(ll n)  {      ll i,ans = n,t = n;      for( i = 2 ; i * i <= n ;i++ )          if( t%i == 0) {              ans -= ans/i;              while( t%i == 0) t /= i;          }      if( t > 1) ans -= ans/t;      return ans;   }    ll gcd(ll a,ll b)  {      return b?gcd(b, a%b):a;  }  ll fast_pow(ll a,ll b,ll c)     //a^b%c  {      ll tmp=a%c,res=1;      while (b) {          if(b&1) res =res*tmp%c;          tmp=tmp*tmp%c;          b>>=1;      }      return res;  }  int main()  {      ll x,y,a;      while (cin>>x>>y>>a) {          ll k=y/(x-1);          if(k==0)    {cout<<"1"<<endl;continue;}//这儿也可以不特判,写的时候是因为快速幂写错WA了以后乱加的<img alt="难过" src="http://static.blog.csdn.net/xheditor/xheditor_emot/default/sad.gif">          ll tmp=gcd(a,k);          if(gcd(a/tmp, x)!=1){ cout<<"Impossible!"<<endl;continue;};          ll res=euler(a/tmp),cnt=0;          for(int i=1;i*i<=res;i++)          {              if(res%i==0)              {                  d[cnt++]=i;                  if(res/i!=i)d[cnt++]=res/i;              }          }          sort(d,d+cnt);          for(int i=0;i<cnt;i++)          {              if(fast_pow(x, d[i],a/tmp)==1)              {                  cout<<d[i]<<endl;                  break;              }          }      }      return 0;  }     

Diophantus of Alexandria

/*x 、y、n都是正整数,并且 显然,x >= n , y >= n ,现在假设 y = n +k (k为正整数) ,那么带入公式,可以得出 x = (n*(n+k))/k = n*n/k + n; 由于x 是正整数,现在的关键问题就是要求出 n*n/ k 有多少组正整数的可能,显然,所要求的就是 n*n 因子的个数// 问题已经非常接近答案了,但是最后还有一个问题,n<= 10^9 , 那么n*n <= 10^18 ,对于一个这么大的数字怎样才能求出它因子的个数呢?命题1: 一个正整数 n 可以用素因子唯一表示为 p1^r1 * p2^r2 * ... pk^rk (其中 pi 为素数) , 那么这个数的因子的个数就是,(r1+1)*(r2+1)*...*(rk+1).如果一个数字 n = p1^r1 * p2^r2 * ... pk^rk ,那么 n*n = p1^r1 * p2^r2 * ... pk^rk * p1^r1 * p2^r2 * ... pk^rk ,它的因子的个数就是 (2*r1+1)*(2*r2+1)*...*(2*rk+1).由1/x+1/y=1/n  简化成(x-n)(y-n)=n*n,    很明显,只要能将n*n进行因数分解,  则其解都满足题意。    假设n可写成:  a1^k1 * a2^k2 * a3^k3 * ... * am^km, (1)  其中a1,a2,a3,...am互质,  则n*n的约数为  (2k1+1)(2k2+1)(2k3+1)...(2km+1), (2)  记其积为O,  从而其存在的解的个数为  [O+1]/2,*/#include<stdio.h>#include<stdlib.h>int  prime(  int num[] )//素数筛选{     int hash[20000]={0};     for( int i=3; i<=200; i+=2 )     {         if( hash[i/2] )           continue;           int x=i<<1;         for( int j=i*i; j<40000; j+=x )            hash[j/2]=1;          }        int count=0;     num[++count]=2;     for( int i=1; i<20000; i++ )     {           if( hash[i]==0 )            num[++count]=(i<<1)+1;         }     return count;}int res( int num[], int count ,int  number ){    int sum=1;    for( int i=1; i<=count; i++ )    {         if( number==1 ) break;        int x=0;        while( number%num[i]==0 )        {           x++;           number/=num[i];               }             sum*=(2*x+1);    }    if( number!=1 )       sum*=3;    return sum;    }int main(){    int n,num[5000],number;    int count=prime( num );    scanf( "%d",&n );    for( int i=1; i<=n; i++ )    {        scanf( "%d",&number );        printf( "Scenario #%d:\n" ,i);        printf( "%d\n",(res( num,count,number )+1)/2  );        puts("");         }    return 0;    }

X问题

/*该题是一道中国剩余定理题目,刚开始我就用了一下暴力的方法 (同时也优化了一下)一下子就超时了,后来看了一下这个题的解题报告才知道要用中国剩余定理,这也是我第一次用中国剩余定理解题。这个定理又叫孙子定理。就是给定几个数  虽然不互质   然后一个数取余他们又有相应的余数。那么这个问题的答案相差的一定是这些数的最小公倍数。首先我们求出最小公倍数K;然后在N%k+1到N%k+k这个范围内暴找一下有没有一个符合所有条件的数,我们把N分成一段段的,因为在每一段一定是没有或者只有一个的。为什么可以分成一段段的,我们知道中国剩余定理求的是最小的数,那么它的倍数同样是符合条件的;所以如果有的话  sum+=n/k;不要忘记,然后判断在1到n%k这个范围内有没有符合条件的答案 ( 因为我们是从数的最后面分段开始的,最前面可能还有数剩余 )。*/#include<stdio.h>#include<stdlib.h>int Gcd( int a, int b ){    b==0?a:Gcd( b, a%b );    }int judge( int a[], int b[], int n, int sum, int N ){    int count=0;    int t=N%sum;    for( int i=t+1;i<=sum+t;i++ )    {         int cnt=0;         for( int j=0; j<n; j++ )         {           if( i%a[j]==b[j] )                cnt++;           else break;         }         if( cnt==n )         {             count+=N/sum;             break;             }    }     for( int i=1;i<=t; i++ )    {            int cnt=0;         for( int j=0; j<n; j++ )         {           if( i%a[j]==b[j] )                cnt++;           else break;         }         if( cnt==n )         {             count++;             break;             }     }    return count;   }int main(  ){   int a[10],b[10];   int n,T,N;   scanf( "%d",&T );   while( T-- )   {       int sum=1;       scanf( "%d%d",&N, &n );       for( int i=0; i<n; i++ )       {            scanf( "%d",&a[i] );            int t=Gcd( sum , a[ i ] );            sum*=(a[i]/t);            }       for( int i=0; i<n; i++ )          scanf( "%d",&b[i] );       printf( "%d\n",judge( a,b,n,sum,N ) );          }return 0;    }

Jacobi symbol

//a^((p-1)/2)%p==1,则有解#include<cmath>  #include<iostream>  using namespace std;  const int maxn=1000000+10;  typedef long long ll;    bool not_pri[maxn];  int cnt=0,pri[maxn>>3];    void init()  {      for(int i=2;i<maxn;i++){          if(not_pri[i]) continue;          pri[cnt++]=i;          for(int j=i+i;j<maxn;j+=i) not_pri[j]=1;      }  }  ll pow(int a,int n,int p) //a^n%p  {      ll base=a,ret=1;      while(n){          if(n&1) ret=ret*base%p;          base=base*base%p;n>>=1;      }return ret;  }  int cal(int a,int p)  {      if(a%p==0) return 0;      return pow(a,p/2,p)==1?1:-1;  }    int main()  {      int a,n;init();      while(cin>>a>>n)      {          int ans=1;          if(not_pri[n])          {              for(int i=0;pri[i]<=n;i++)              {                  if(n%pri[i]) continue;                  int cnt=0;                  while(n%pri[i]==0) n/=pri[i],cnt++;                  int tmp=cal(a,pri[i]);                  if(tmp==-1&&cnt%2==0) tmp=1;                  ans*=tmp;              }          }          else ans=cal(a,n);          cout<<ans<<endl;      }      return 0;  }  


原创粉丝点击