/*定义: 对于正整数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; }