HDU 1042(N! 简单的大数问题)
题目分析:这个题目很简单,但是有点细节问题,我在代码里注释一下 就可以了
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int main(){ int i,j,n,t,lenth,carry,tempt; while(scanf("%d",&n)!=EOF){ int s[7201]={1}; lenth=1; //lenth 记录数组的实际运用长度 for(i=2;i<=n;i++){ carry=0; for(j=0;j<lenth;j++){ tempt=s[j]*i+carry; carry=tempt/100000; s[j]=tempt%100000; } while(carry){ s[lenth++]=carry%100000; //如果有进位 数组运用的实际长度+1 carry/=100000; } } j=lenth-1; printf("%d",s[j--]); //注意看好这一步!最高位的前置0不能输出,其他的前 //置0要输出 for(;j>=0;j--) printf("%05d",s[j]); printf("\n"); } return 0; }
POJ(1061 青蛙的约会 扩展欧几里德求解不定方程)
题目大意:
Description
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的 数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。 现在要你求出它们跳了几次以后才会碰面。
Input
会很大的!所以只能 用 扩展的欧几里德 求这个不定方程!
第一步里面 化简后的标准方程的一组解麽?然后我已经求得 标准方程的一组解了,继续讨论 解系里面的最小值吧!
#include<iostream> #include<cmath> #include<cstdio> using namespace std; long long gcd(long long a,long long b){ long long c; if(a<b){ c=a; a=b; b=c; } while(b){ c=b; b=a%b; a=c; } return a; } long long extended_gcd(long long a,long long b,long long &x,long long &y){ long long ans,t; if(b==0){ x=1; y=0; return a; } else{ ans=extended_gcd(b,a%b,x,y); t=x; x=y; y=t-(a/b)*y; } return ans; } int main(){ long long x,y,m,n,l; long long a,b,d,k,s,t; while(scanf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&l)!=EOF){ a=n-m; b=l; d=x-y; long long r=gcd(a,b); if(d%r!=0){ printf("Impossible\n"); continue; } a/=r; b/=r; d/=r; extended_gcd(a,b,s,k); s=s*d; k=k*d; t=s/b; s=s-t*b; if(s<0) s+=b; printf("%lld\n",s); } return 0; }
hit2813 Garden visiting 关于组合数的一个简单的拆分求解
题目大意:给你n * k 的矩形,始点在矩形的左上角,出口在矩形的右下角,求出最快走出这个矩阵的,方案数。
题目分析:最短的走出矩阵的方案的路程肯定是N+k,然后我们把这个路程离散化成N+K个状态,但是每种走法,的起点,跟终点的状态都一样的,只不过N+K-2里面有不同的走法, 我们换位思考一下,在N+K-2个状态里有有K-1个状态 分别投影在 K边的不同的点上面,因为,你必须往下移动K-1个单位才能到达出口,所以在竖直方向上必须有K-1个状态分别投影在K边的不同点上,这样我们就得出了方案数的组合数,就是在N+K-2里面选k-1个!公式:
计算方面就是把这个组合数分别分解成素数,然后对素数进行求解,取模!
代码+部分注释:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int pri[20000],num[20000],cnt,p; bool flag[200001]; void get_pri(){ memset(flag,true,sizeof(flag)); int i,j; pri[1]=2; cnt=1; for(i=4;i<=200000;i+=2) flag[i]=false; for(i=3;i<=3000;i+=2) for(j=i*i;j<=200000;j+=2*i) flag[j]=false; for(i=3;i<=200000;i+=2) if(flag[i]) pri[++cnt]=i; return ; } int paw(int a,int t){ int ans=1; while(t){ if(t%2==1) ans=(ans*a)%p; a=(a*a)%p; t/=2; } return ans; } int solve(){ int cas,n,k,c,s,i; long long ans; scanf("%d",&cas); while(cas--){ scanf("%d %d %d",&k,&n,&p); if(k>n){ c=k; k=n; n=c; } n=k+n-2; k--; if(k==n){ printf("%d\n",1%p); continue; } for(i=1;i<=n&&pri[i]<=n;i++) num[i]=0; for(i=1;pri[i]<=n&&i<=cnt;i++){ s=pri[i]; while(n>=s){ num[i]+=n/s-(n-k)/s-k/s; s*=pri[i]; } } ans=1; for(i=1;i<=cnt&&pri[i]<=n;i++){ if(num[i]>0) ans=(ans*paw(pri[i],num[i]))%p; if(ans==0) break; } printf("%lld\n",ans); } return 0; } int main(){ get_pri(); solve(); return 0; }
FOJ(1753 Another Easy Problem 又一种整数分解)
题目大意:就是给你多个组合数,让你求出
C(n1,m1)==0 (mod M)
C(n2,m2)==0 (mod M)
C(n3,m3)==0 (mod M)
的最大的M满足上式,
题目分析:
其实就是把这些组合数,分解成素数因子,然后求每个因子出现的最少的次数,题目不难 但时间卡的好紧,我超时得想吐,如果您也遇到跟我一样的情况,请你细细看我 代码提示的各个优化环节!不懂的 留言,我们一起讨论
代码+部分注释:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int pri[1000011]; bool flag[1000001]; int sim[1000001]; int num1[151],num2[151]; int cnt; void get_pri(){//生成素数表 for(int i=1;i<=1000000;i++) flag[i]=true; pri[1]=2; cnt=1; flag[0]=flag[1]=false; for(int i=4;i<=1000000;i+=2) flag[i]=false; for(int i=3;i<3000;i+=2) for(int j=i*i;j<=1000000;j+=2*i) flag[j]=false; for(int i=3;i<=1000000;i+=2) if(flag[i]) pri[++cnt]=i; return ; } int paw(int a,int t){ int ans=1; while(t){ if(t%2==1) ans=ans*a; a=a*a; t/=2; } return ans; } void solve(){ int cas,n,k,i,s,j,t; bool flag; int maxn; while(scanf("%d",&cas)!=EOF){ flag=false; maxn=0; for(i=1;i<=cas;i++){ scanf("%d %d",&num1[i],&num2[i]); if(maxn<num1[i]) maxn=num1[i]; if(num1[i]==num2[i]) flag=true; //如果哦值为1的情况,就不用计算了,肯定就是1啦! } if(flag){ printf("1\n"); continue; } for(j=0;j<=100000;j++) sim[j]=2147483647; for(t=0;t<cas;t++){ n=num1[t+1]; k=num2[t+1]; for(i=1;i<=cnt&&pri[i]<=maxn;i++){ if(sim[i]==0)//这也是一个优化的地方,如果是0, //这个素数在各个组合数里出现的最少次数肯定是0啦 continue; int sum=0; int p=pri[i]; while(n>=p){//这个就是计算各个素数在组合数里出现的次数 sum=sum+n/p-k/p-(n-k)/p; p*=pri[i]; //记得一次把这个写错了写成“p*=p”悲剧哦,段错误,肯定超出int范围啦! } if(sum<sim[i]) sim[i]=sum; //sim[i]是记录第i个素数在各个组合数里出现的最少的素数! } } long long ans=1; //下面这个循环就无非是 计算每个组合数里,各个素数出现的最少次数, //组合成的数ans,也就是题目要求的数! for(i=1;i<=cnt&&pri[i]<=maxn;i++){ if(sim[i]>0&&sim[i]!=2147483647){ ans=ans*paw(pri[i],sim[i]); } } printf("%lld\n",ans); } return ; } int main(){ get_pri(); solve(); return 0; }
PKU (2992 Divisors 整数分解的经典应用!)
题目大意:就是给你一个组合数 这里0=<k=<n=<341
题目分析:
1.约数个数定理:
设n的标准质因数分解为n=p1^a1*p2^a2*...*pm^am,
则n的因数个数=(a1+1)*(a2+1)*...*(am+1).
2. 对于任意质数p,n!中有(n/p+n/p^2+n/p^3+...)个质因子p。
代码+部分注释:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int pri[200]; bool flag[532]; int cnt; void get_pri(){ for(int i=1;i<=431;i++) flag[i]=true; pri[1]=2; cnt=1; flag[0]=flag[1]=false; for(int i=4;i<=431;i+=2) flag[i]=false; for(int i=3;i<23;i+=2) for(int j=i*i;j<=431;j+=2*i) flag[j]=false; for(int i=3;i<=431;i+=2) if(flag[i]) pri[++cnt]=i; return ; } int main(){ int n,k,s,i; int num[100]; get_pri(); while(scanf("%d %d",&n,&k)!=EOF){ memset(num,0,sizeof(num)); for(i=1;i<=cnt;i++){ s=n; if(s<pri[i]) break; while(s){ num[i]+=s/pri[i]; s/=pri[i]; } } for(i=1;i<=cnt;i++){ s=n-k; if(s<pri[i]) break; while(s){ num[i]-=s/pri[i]; s/=pri[i]; } } for(i=1;i<=cnt;i++){ s=k; if(s<pri[i]) break; while(s){ num[i]-=s/pri[i]; s/=pri[i]; } } long long sum=1; for(i=1;i<=cnt;i++) if(num[i]>0) sum*=num[i]+1; printf("%lld\n",s); } return 0; }
POJ (1811 Prime Test 关于miller_rabin的素数判断和pollard_rho的整数分解)
题目大意:就是给一个很大的数,让你判断是否为素数,如果不是则计算出小于等于他的最小的素数。2 <= N < 254
题目分析: 这里由于数字非常大,我们不能按照常规思路来做,
miller_rabin的素数判断:Fermat小定理:若n为素数,则,有an-1≡1(mod n),大部分的都满足=1(mod n),但是这不能包含所有情况,所以我们利用随机函数,来随机产生在2~n-2直接的数进行测试,如果没测出来,几乎就可以判断这个数是素数,这样做n太大了,计算起来肯定会超时的,要知道n的范围:2~2^54 这样太可怕了,这样的话我们来优化:
首先n-1= m*2q ,2^(n-1)=(2^m)^2q 在这里求m,与q应该是非常简单的事情吧,然后我们怎么办呢?这里我可以利用随机数a枚举
(2^m)^2q (q=1..q-1),因为这些式子是 2^(n-1)的因数,如果他们不是素数,那么证明N不是素数,反之是素数(只是说概率很大可以 看作是了),
这里还有一种情况,就是该数不是素数,然后就要分解整数,求出最小的一个素数,这里分解整数,用到的是pollard_rho分解法:
这里证明我就不写了,水平有限,只写过程:
首先我们用随机函数生成一个x1(1<x1<n),然后根据x1 构造出x2 ( f[x]=x1^2+c )然后 这里假设有p 能够整除 x1-x2 ,x1-x2=p*q,这里q显然可以整除x1-x2,但是x1-x2不能整除n,从而可以得到,q不能整除 n,所以gcd( (x1-x2) ,n )有可能得到1,也有可能得到n的一个因数,所以我们进行随机枚举,这样就可以计算出n的因数分解形式。
代码+部分注释:
#include<iostream> #include<cstdio> #include<ctime> #include<cstdlib> using namespace std; long long top=0,pri[100]; long long multiply( long long a,long long b,long long n){ long long exp,res=0; //这个就是用加法,来模拟乘法,为了防止运算的数太大, exp=a%n; while(b){ if(b&1){ res+=exp; if(res>n) res-=n; } exp<<=1; if(exp>n) exp-=n; b/=2; } return res; } long long paw(long long a,long long t,long long n){ long long ans=1; a=a%n; while(t){ if(t%2==1) ans=multiply(ans,a,n); a=multiply(a,a,n); t/=2; } return ans; } long long gcd(long long a,long long b){ long long c; if(a<b){ c=a; a=b; b=c; } while(b){ c=b; b=a%b; a=c; } return a; } bool miller_rabin(long long n,long long ti){ if(n==2) return true; if(n<2||!(n&1)) return false; long long p=n-1; long long k=0,a,x; while(p%2==0){ p/=2; k++; } long long ans; srand(time(0)); for(int t=1;t<=ti;t++){ a=rand()%(n-1)+1; x=paw(a,p,n); for(int i=1;i<=k;i++){ ans=multiply(x,x,n); if(ans==1&&x!=1&&x!=n-1) return false; x=ans; } if(ans!=1) //注意这个判断! return false; } return true; } long long pollard_rho(long long a,long long k,long long n){ srand(time(0)); long long x=rand()%(n-1)+1; long long i=1,t=2; long long y=x; while(1){ i++; x=(multiply(x,x,n)+k)%n; long long ans=gcd(y-x,n); if(ans>1&&ans<n) return ans; if(x==y) return n; if(t==i){ y=x; t*=2; } } } void search(long long n,long long k){ if(miller_rabin(n,10)){ pri[++top]=n; return ; } else{ long long p=n; while(p>=n) p=pollard_rho(p,k--,n); search(p,k); search(n/p,k); } } int main(){ int cas,i; long long n; cin>>cas; while(cas--){ scanf("%lld",&n); if(miller_rabin(n,10ll)) printf("Prime\n"); else{ top=0; search(n,180); long long min=-1; for(i=1;i<=top;i++){ if(min<0||min>pri[i]) min=pri[i]; } printf("%lld\n",min); } } return 0; }
ZOJ(2562 More Divisors 求反素数经典应用!)
题目大意:给一个N求不超过N的 哪个数的因子数最多,数目相同的取值小的那个!
题目分析:这里引进反素数知识:
反素数第一点:g(x)表示 x含有因子的数目,设 0<i<=x 则g(i)<=x;
反素数第二个特性:2^t1*3^t2^5^t3*7^t4..... 这里有 t1>=t2>=t3>=t4...
所以我们根据这两个属性 进行递归求解!
递归过程:
首先做出三条判断,第一是否该值不超过N的大小,第二比较因子的个数,更新最大的,即更新最大的反素数,第三如果因子数相等,则更新较小的。
然后枚举+递归不断尝试更新,使得得到因子数最大的那个!
代码+部分注释:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; long long bestres,bestsum,n; long long pri[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; //res表示当前讨论的值,sum表示该值的因子数目, //k表示讨论到第几个素数了,limit表示上次的幂的值 //这次幂的上限,就是体现t1>=t2>=t3... void work(long long res, long long sum,int k,int limit ){ if(res>n)//如果超过n就跳出! return ; if(sum>bestsum){ //这次计算的因子数目比我以前过程的最大值还大,那么就要更新! bestres=res; bestsum=sum; } if(sum==bestsum&&res<bestres){ //如果因子数目一样,取值比较小的! bestres=res; } long long p=pri[k]; //记得第一次p=pri[k],因为下面i是从1开始计数的! for(int i=1;i<=limit;i++,p*=pri[k]){ if(res*p>n) break; else work(res*p,sum*(i+1),k+1,i); } } int main(){ while(scanf("%lld",&n)!=EOF){ bestres=1LL; bestsum=1LL; work(1LL,1LL,0,50); printf("%lld\n",bestres); } return 0; }
POJ (2262 Goldbach's Conjecture)
题目大意:给你一个大于等于6的偶数,让你求出两个奇素数,a,b 满足 x=a+b; a,b也许有多对,但是只需要求出 b-a最大的那对!
题目分析: 这题直接暴力就可以了,先求出奇素数,第一队就是满足条件的了
代码://pku 2909 pku2262 hdu1397 AC代码都差不多
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int s[500020]; int flag[1000002]={0}; void init(){ int k=1; for(int i=2;i<=1000000;i+=2) flag[k]=1;//“0”代表是奇素数,”1“代表不是! for(int i=3;i<=1000000;i+=2){ if(!flag[i]){ s[k++]=i; for(int j=2*i;j<=1000000;j+=i) flag[j]=1; } } } int main(){ init(); long n,i; while(scanf("%ld",&n),n){ if(n%2==1) continue; long long p=n/2+1; for(i=1;s[i]<=p;i++){ int k=n-s[i]; if(!flag[k]){ printf("%ld = %d + %d\n",n,s[i],k); break;//注意第一对就是题目要的,这里要直接跳出了 不然TLE } } if(i>p) printf("Goldbach's conjecture is wrong.\n");//这里要不要无所谓 其实 } return 0; }
PKU (1730 Perfect Pth Powers)
题目分析: 就是给你 X 求 最大的p满足 X=a^p 特别提醒一下 X可正可负!
这里我用的是暴力,就是第一种 枚举+二分,第二种是 比较高效的暴力0MS过的!
这里唯一要考虑的是,首先 如果是正值我们不用做任何处理,如果是负数, 那么他的p肯定不能是偶数,如果是偶数,p次方后 肯定是整数,所以会与答案 有 冲突, 如果还有不懂的 请您留言
代码
#include<stdio.h> #include<cmath> #include<iostream> using namespace std; int main() { int i; double n; while(cin>>n,n){ int maxn=1; bool flag=false; if(n<0){ n*=-1; flag=true; } for(i=32;i>=1;i--){ double up=ceil(pow(n,1.0/i)); double down=floor(pow(n,1.0/i)); if(fabs(n-pow(up,i))<1e-8||fabs(n-pow(down,i))<1e-8) { if(flag){ if(i%2==1){ maxn=i; break; } } else { maxn=i; break; } } } printf("%d\n",maxn); } return 0; }
#include<stdio.h> #include<cmath> #include<iostream> using namespace std; int main() { long long n,i,t,maxn; while(cin>>n,n){ maxn=0; bool flag=false; if(n<0){ n*=-1; flag=true; } long long k=(long long)sqrt(n); for(i=2;i<=k;i++){ long long m=n; t=0; while(m%i==0){ t++; m/=i; } if(m==1){ if(flag){ if(t%2==1) maxn=maxn>t? maxn:t; else continue; } else maxn=maxn>t? maxn:t; } } if(maxn==0) maxn=1; printf("%lld\n",maxn); } return 0; }
PKU( 2191 Mersenne Composite Numbers )
题目大意:
#include<stdio.h> int main() { int n,i; int b[10]={11,23,29,37,41,43,47,53,59}; char a1[10][100] ={"23 * 89 = 2047 = ( 2 ^ 11 ) - 1", "47 * 178481 = 8388607 = ( 2 ^ 23 ) - 1", "233 * 1103 * 2089 = 536870911 = ( 2 ^ 29 ) - 1", "223 * 616318177 = 137438953471 = ( 2 ^ 37 ) - 1", "13367 * 164511353 = 2199023255551 = ( 2 ^ 41 ) - 1", "431 * 9719 * 2099863 = 8796093022207 = ( 2 ^ 43 ) - 1", "2351 * 4513 * 13264529 = 140737488355327 = ( 2 ^ 47 ) - 1", "6361 * 69431 * 20394401 = 9007199254740991 = ( 2 ^ 53 ) - 1", "179951 * 3203431780337 = 576460752303423487 = ( 2 ^ 59 ) - 1"}; scanf("%d",&n); for(i=0;i<9;i++) { if(n>b[i]) printf("%s\n",a1[i]); else break; } return 0; }