ACM Steps_Chapter Seven_Section2

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 100000000
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; 
} 

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;  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值