POJ 数论入门
题意:对于给定的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)的个数为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; }
题目链接: 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); } }
题目链接: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; } }
题目链接: 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互素,故用中国剩余定理即可
#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
#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; } }