POJ 数论入门

poj3641 Pseudoprime numbers

题目链接: http://poj.org/problem?id=3641

题意:对于给定的a和p,判断$a^p\%p=a\%p$是否成立(p必须为素数)

思路:快速幂求$a^p$,由于数据量较小故$O(n^1/2)$判断素数即可

#include<cstdio>
#include<iostream>
using namespace std;
int powmod(int a, int n, int m){
    if (n == 0) return 1;
     int x = powmod(a, n/2, m);
    long long ans = (long long)x * x % m;
    if (n & 1) ans = ans * a % m;
    return (int)ans;
}
int main(){
int p,a,i;
  while((cin>>p>>a)&&(p!=0||a!=0)){
        for( i=2;i*i<p;i=i+1){
            if(p%i==0)
             break;
        }
    if(i*i>=p){
        cout<<"no"<<endl;
        continue;
     }
    if(powmod(a,p,p)==a%p)
      cout<<"yes"<<endl;
    else cout<<"no"<<endl;
  }
}

poj2478 Farey Sequence 欧拉函数

题目链接: http://poj.org/problem?id=2478

题意:定义数列F(n)为所有满足$0<a<b<=n<=10^6$,且$gcd(a,b)=1$的分数a/b的集合(a,b为整数),输入n,输出F(n)的个数;

根据题意易知F(n)的个数为F(n-1)的个数加上小于n且与n互质的数个的数,即为n的欧拉函数。由于n的范围较大,故用筛选法打个n以内的欧拉函数表,再递推一下即可,注意可能会爆int $O(n*log n)$

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define Max 1000005
long long int euler[Max];
int main(){
     euler[1]=1;
     for(int i=2;i<Max;i++)
       euler[i]=i;
     for(int i=2;i<Max;i++)
        if(euler[i]==i)
           for(int j=i;j<Max;j+=i)
              euler[j]=euler[j]/i*(i-1);
              for(int i=3;i<=1000000;i++)
                  euler[i]=euler[i-1]+euler[i];
	int n;
	while((cin>>n)&&n!=0)
    	cout<<euler[n]<<endl;
}

poj1995 Raising Modulo Numbers 快速幂

题目链接: http://poj.org/problem?id=1995

题意:n组数据,每组数据给一个M和m组数:$a1...an,b1...bn$,求$(a_1^{b_1}+a_2^{b_2}+...+a_n^{b_n})\%n$

主要是快速幂(分治)求$a_i^{b_i}$,注意每求一次都对M 取一次模再求和避免溢出

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int powmod(int a, int n, int m){
    if (n == 0) return 1;
     int x = powmod(a, n/2, m);
    long long ans = (long long)x * x % m;
    if (n & 1) ans = ans * a % m;
    return (int)ans;
}
int main(){
int n;
scanf("%d",&n);
 while(n--){
    int x[45005],y[45005];
    int h,m,sum=0;
    scanf("%d%d",&m,&h);
    for(int i=0;i<h;i++){
        scanf("%d%d",&x[i],&y[i]);
        sum=((sum+powmod(x[i],y[i],m))%m);
    }
    printf("%d\n",sum);
  }
}

poj1664 放苹果 母函数

题目链接:http://poj.org/problem?id=1664

母函数模板题:M个相同球放到N个相同的盒子中 $G(x) = (1+x+x^2+x^3+...x^k+...)(1+x+x^2+x^3+...x^k+...)$...(一共有 n 项)我们要求的就是x^m前面的系数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<cmath>
using namespace std;
int n;
struct Z{
int val;
int num;
}z[10000];
int ans[100005],ans0[100005];
int main(){
    int t;
    cin>>t;
    while(t--){
        int m,n;
        cin>>m>>n;
        for(int i=0;i<=m;i++){
            ans[i]=1;
            ans0[i]=0;
        }
        for(int i=2;i<=n;i++){
          for(int j=0;j<=m;j++){
            for(int k=0;j+k<=m;k+=i)
                ans0[j+k]+=ans[j];
          }
            for(int j=0;j<=m;j++){
                ans[j]=ans0[j];
                ans0[j]=0;
            }

        }
        cout<<ans[m]<<endl;
    }
}

poj2084Game of Connections 卡特兰数

题目链接: http://poj.org/problem?id=2084

题意:圆周上有标号为1,2,3...,2*n的共计2n个点,这2n个点配对可连成n条弦,且n条弦两两不相交,问共有多少连接方式

卡特兰数$C(n)=2(2n-1)*C(n-1)/n+1。。。。$(为啥呢==)

大数要处理,Java可以,但是不会啊,只有用笨办法了,用个数组存大数会处理进位就行

#include<cstdio>
#include<iostream>
using namespace std;
int num[105][105];
int main(){
 num[0][1]=1;
 num[1][1]=1;
 int k=1;
 for(int i=2;i<=100;i++){
    int mul=4*i-2,j=1,x;
    while(j<=k){
        int z=mul*num[i-1][j];
        x=j;
        num[i][x]+=z%10;
        num[i][x+1]+=z/10;
        for(int x=j,y=1;y<=5;y++)
            if(num[i][x]>=10){
                    num[i][x+1]+=(num[i][x]/10);
                    num[i][x]=num[i][x]%10;
                    x++;
                }

        j++;
    }
    for(int p=100;p>=1;p--)
        if(num[i][p]!=0){
        k=p;
        break;
        }
    j=k;
  while(j>=1){
    num[i][j-1]+=(num[i][j]%(i+1)*10);
    num[i][j]/=(i+1);
    j--;
  }
 }
 int n;
 while((cin>>n)&&n!=-1){
  for(int i=100;i>=1;i--){
    if(num[n][i]!=0){
        for(int j=i;j>=1;j--){
            cout<<num[n][j];
        }
        cout<<endl;
        break;
    }

 }
}
}

poj2356 Find a multiple 卡特兰数

题目链接: http://poj.org/problem?id=2356

题意:给你n个不大于15000的自然数,其中有些数可能相同,让你在其中选择一些数让他们的总和是n的倍数,若存在,输出选择数的个数和这些数,不存在则输出0

n<=10000;

设n个数分别为$a_1,a_2...a_n$,前缀和分别为$s_1,s_2...s_n$,让前缀和对n取模,则有n个取模后的数,这些数一定在0~n-1之间,由抽屉原理,这些数中一定有两个数相同,故答案即为这两个前缀之间的数,所以不存在输出0的情况

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int main(){
int n;
cin>>n;
    int s,e;
        int rem[10005]={0},z[10005]={0},a[10005];
    for(int i=1;i<=n;i++)
         scanf("%d",&a[i]);
     for(int i=1;i<=n;i++){
        z[i]=z[i-1]+a[i];
        z[i]=z[i]%n;
        if(z[i]==0){
            s=1;
            e=i;
            break;
        }
         else if(rem[z[i]]){ 
            s=rem[z[i]]+1;
            e=i;
            break;
         }
         else rem[z[i]]=i;

        }
        printf("%d\n",e-s+1);
        for(int i=s;i<=e;i++)
            printf("%d\n",a[i]);

}

poj1006 Biorhythms 中国剩余定理

题目链接: http://poj.org/problem?id=1006

题意:人类从一开始有三个生物周期:体力、情绪和智力,周期分别为23、28和33天因而三个生物周期产生的高潮不同,已知每个生物周期上次高潮距离年初的天数d,以及从年初开始的一个天数(起始时间),问你下次生物周期高潮同时产生距离起始时间的天数

设下次高潮距离年初时间为x,m1=23,m2=28,m3=33 赏赐高潮产生的时间为b1,b2,b3

则由题意知:xi≡bi(mod mi)

由于m1,m2,m3互素,故用中国剩余定理即可

注意x可能小于d

#include<cstdio>
#include<iostream>
using namespace std;
int x,y;
int Extended(int a,int b){
 if(b==0){
    x=1;
    y=0;
    return a;
 }
 int d=Extended(b,a%b);
 int temp=x;
 x=y;
 y=temp-a/b*y;
 return d;
}
int main(){
  int cas=0;
  int M=23*28*33;
  int p,e,i,d;
  while((cin>>p>>e>>i>>d)&&(p!=-1||e!=-1||i!=-1||d!=-1)){
  int a[4]={p,e,i};
  int m[4]={23,28,33};
  int ans=0;
  for(int i=0;i<3;i++){
    int m0=M/m[i];
    Extended(m0,m[i]);
    ans=(ans+a[i]*m0*x)%M;
  }
  if(ans<=d)
    ans+=M;
  ans=ans-d;
  cout<<"Case "<<++cas<<": the next triple peak occurs in "<<ans<<" days."<<endl;
  }
}

poj1061 青蛙的约会

题目链接: http://poj.org/problem?id=1061

设t是跳的步数 由于每走L米就回原点,故可以建立一个同余方程(x+mt)≡(y+nt)%L  =>(x+mt)-(y+nt)=kL => x-y=kL-(n-m)t,用欧几里得求最小的正整数t即可

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
long long x,y;
long long gcd(long long a,long long b){
   if(b==0)
      return a;
    return gcd(b,a%b);
}
long long Extended(long long a,long long b){
   if(b==0){
    x=1;y=0;
    return a;
   }
  long long d=Extended(b,a%b);
  long long temp=x;
  x=y;
  y=temp-a/b*y;
  return d;
}
int main(){
long long p,q,m,n,l,t1,t2;
cin>>p>>q>>m>>n>>l;
t1=t2=1e15;
 if((p-q)%gcd(l,m-n)==0){
    long long d=Extended(l,n-m);
    long long t=l/d;
    y=y*(p-q)/d;
    t1=(y%t+t)%t;
 }
 if(t1==1e15)
    cout<<"Impossible"<<endl;
 else cout<<t1<<endl;
}

poj2891 Strange Way to Express Integers 同余方程组

题目链接: http://poj.org/problem?id=2891

题意:给你k对整数(ai,ri),已知对于每对整数,m%ai=ri,问你求m的最小值,不存在输出-1

即解同余方程组m≡ri(mod ai) 合并方程即可

#include<cstdio>
#include<iostream>
#include<algorithm>
typedef long long int ll;
using namespace std;
ll x,y;
ll Extended(ll a,ll b){
  if(b==0){
    x=1;y=0;
    return a;
  }
  ll d=Extended(b,a%b);
  ll temp=x;
  x=y;
  y=temp-a/b*y;
  return d;
}
ll gcd(ll a,ll b){
if(b==0)
    return a;
return gcd(b,a%b);
}
int main(){
   int k;
  while(cin>>k){
   ll b1,b2,m1,m2;
   cin>>m1>>b1;
   bool f=1;
   for(int i=1;i<k;i++){
    cin>>m2>>b2;
    if(f==1){
    ll m=gcd(m1,m2);
    if(((b2-b1)%m)!=0)
        f=0;
    else  {
          ll d=(b2-b1)/m;
          Extended(m1,m2);
          ll t=m2/m;
          ll c=((x%t)*(d%t)%t+t)%t;
          b1=b1+c*m1;
          m1=m1*m2/m;

    }
   }
  }
  if(f==1){
    cout<<b1<<endl;
  }
  else cout<<-1<<endl;
   }
}

  

 

 

 

 

 

 

posted @ 2022-03-29 14:45  dlutjwh  阅读(79)  评论(0编辑  收藏  举报