一、同余定理
简单粗暴的说就是:若 a-b == m 那么 a%m == b%m
这个模运算性质一眼看出。。。直接上入门水题:
附AC代码(这个也没啥模板。。。。知道就好)
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<stdlib.h>
- #define mem(a,x) memset(a,x,sizeof(a))
- using namespace std;
- typedef long long ll;
- /*
- 同余:
- if
- a-b == m
- then
- a%m == b%m
- */
- const int N = 1000000;
- bool vis[N+5];
- int a[N+5];
- bool ol[N+5];
- int main()
- {
- int T;cin>>T;
- while (T--)
- {
- int n;
- scanf("%d",&n);
- mem(vis,0);
- for (int i = 1;i <= n;++i) scanf("%d",a+i);
- for (int i = 1;i <= n;++i)
- {
- for (int j = i+1;j <= n;++j)
- {
- int d = abs(a[i]-a[j]);
- vis[d] = 1;
- }
- }
- bool fd = 0;
- for (int m = 1;!fd;++m)
- {
- if (!vis[m])
- {
- mem(ol,0);bool ok = 1;
- for (int i = 1;ok&&i <= n;++i)
- {
- if (ol[a[i]%m]) ok = 0;
- else ol[a[i]%m] = 1;
- }
- if (ok)
- {
- printf("%d\n",m);
- fd = 1;
- }
- }
- }
- }
- return 0;
- }
二、扩展欧几里德算法
这个扩展是从原欧几里德算法扩展而来,这个算法真心非常有用!非常有用!非常有用!(中国剩余定理也要用到它)
首先说欧几里德算法(其实就是我们小时候数学课上就学过的辗转相除法求gcd)
欧几里德说:gcd(a,b) = gcd(b,a%b)
于是得到欧几里德算法:
- int gcd(int a,int b)
- {
- return b==0?a:gcd(b,a%b);
- }
PS:__gcd(a,b)库函数可以直接调用,但是有些OJ上提交会CE
现在,我们令a,b的最大公因数为gcd,那么我们一定可以找到一组x,y满足:(这个是欧几里德的定理一)
- a*x + b*y = gcd;
此时,我们设x0,y0是上述不定方程的特解,那么就可以用x0,y0表示出整个不定方程的通解
- x = x0 + (b/gcd)*t;
- y = y0 - (a/gcd)*t;
然后是怎么求解通解x,y?
倒过去看看欧几里德算法,显然,它的结束条件是 b = 0的时候返回a
这就意味着,终止状态是 :a = gcd ,b = 0;
将这组a,b代会不定方程ax+by=gcd,可以得到一组特解:x = 1,y = 0
找到终止状态,我们再看看递归的过程:
- gcd = b*x1 + (a%b)*y1
- = b*x1 + (a-(a/b)*b)*y1 // a%b = a-(a/b)*b
- = b*x1 + a*y1 - (a/b)*b*y1
- = a*y1 + b*(x1-a/b*y1)
最后得到gcd = a*y1 + b*(x1-a/b*y1)和原来的式子gcd = a*x + b*y一对比就得到:
- x = y1,y = x1 - a/b*y1;
上面两个等式很重要!!(因为模板里面会直接用到,如果是直接背模板就得把它背熟)
下面就是扩展欧几里德算法:
- int e_gcd(int a,int b,int &x,int &y)
- {
- if (b == 0)
- {
- x = 1,y = 0;
- return a;
- }
- int ans = e_gcd(b,a%b,x,y);
- int tmp = x;
- x = y;
- y = tmp - a/b*y;
- }
这个算法本质上还是求a,b的最大公因数,只是在计算的过程中顺带计算x,y(同时体验了一把引用的神奇)
扩展欧几里德算法有什么用?反正用法很多,它可以求解形如 ax+by=c的通解
但是实际应用中通常指要求在通解中选一些特殊的解,
比如一个数对于另一个数的乘法逆元。那么什么是乘法逆元?
- ax ≡ 1(mod b) // ax % b = 1
ax%b = 1,可以化成 ax = by + 1
显然,它等价于这样的表达式:ax + by = 1
这个式子很眼熟有木有!!!如果等式右边是gcd(a,b)就好了!!!
然后这里copy欧几里德的三个定理:
- 定理一:如果d = gcd(a,b),则必能找到正的或负的整数x和y使d = ax + by
- 定理二:若gcd(a,b) = 1,则方程ax≡c(mod b)在[0,b-1]上有唯一解
- 定理三:若gcd(a,b) = d,则方程ax≡c(mod b)在[0,b|d01]上有唯一解
于是,对上述方程 ax + by = 1, 当gcd(a,b) != 1的时候无解
故方程ax + by = c 有解的条件是 : c%gcd(a,b) = 0;
按乘法逆元讲,一般,我们能找到无数组解满足条件,但一般题目是求解最小的那组解
假设我们求出了特解x0,那么 ,只需要用x0 % b就是最小解了
为什么?(这个我没管,反正知道是这个。。。。后面贴的参考资料链接里面有。。。)
另外,有的时候求得的特解可能是个负数,或者说b是负数,怎么办?
如果b是负数,取b的绝对值
如果x0是负数,让x0对|b|取模后再加上|b|
然后,直接上模板代码:
- int Cal(int a,int b)//求最小的x使ax+ by = 1
- {
- int x,y;
- int gcd = e_gcd(a,b,x,y);
- if (1%gcd) return -1;//无解
- x*=1/gcd;
- b = abs(b);
- int ans = x%b;
- if (ans <= 0) ans += b;
- return ans;
- }
下面给出求最小逆元的代码:
- typedef long long ll;
- ll e_gcd (ll a, ll b, ll& x, ll& y)
- {
- if (b == 0)
- {
- x = 1, y = 0;
- return a;
- }
- ll ans = e_gcd (b, a % b, y, x);
- y -= a / b * x; //这个和前面用的方法不一样,不过是对的,写起来更快、
- return ans;
- }
- ll Cal(ll a,ll b,ll c)//求最小的x使ax+ by = c
- {
- ll x,y;
- ll gcd = e_gcd(a,b,x,y);
- if (c%gcd) return -1;//无解
- x*=c/gcd;
- b /= gcd;
- if (b < 0) b = -b;
- ll ans = x%b;
- if (ans <= 0) ans += b;
- return ans;
- }
来一道入门级的裸的扩展欧几里德的题感受下模板怎么用:
PS:方程还是要自己列,然后用扩展欧几里德求解
AC代码供参考:
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cmath>
- #define mem(a,x) memset(a,x,sizeof(a))
- using namespace std;
- typedef long long ll;
- ll e_gcd(ll a,ll b,ll &x,ll &y)
- {
- if (b == 0)
- {
- x = 1,y = 0;
- return a;
- }
- ll ans = e_gcd(b,a%b,x,y);
- ll tmp = x;
- x = y;
- y = tmp - a/b*y;
- return ans;
- }
- ll cal(ll a,ll b,ll c)//求最小的x使ax+by=c
- {
- ll x,y;
- ll gcd = e_gcd(a,b,x,y);
- if (c%gcd != 0) return -1;
- x *= c/gcd;
- b/=gcd;
- if (b < 0) b = -b;
- ll ans = x%b;
- if (ans <= 0) ans += b;
- return ans;
- }
- int main()
- {
- ll xa,xb,va,vb,L;
- while (~scanf("%lld %lld %lld %lld %lld",&xa,&xb,&va,&vb,&L))
- {
- ll ans = cal(vb-va,L,xa-xb);
- if (ans == -1) puts("Impossible");
- else printf("%lld\n",ans);
- }
- return 0;
- }
三、中国剩余定理
- x ≡ a1 (mod m1) x%m1 = a1
- x ≡ a2 (mod m2) x%m2 = a2
- (S):x ≡ a3 (mod m3) -> x%m3 = a3
- ... ...
- x ≡ an (mod mn) x%mn = an
有解的判定条件,并用 构造法给出了在有解的情况下解的具体形式
- typedef long long ll;
- ll e_gcd (ll a, ll b, ll& x, ll& y)
- {
- if (b == 0)
- {
- x = 1, y = 0;
- return a;
- }
- ll ans = e_gcd (b, a % b, y, x);
- y -= a / b * x; //这个和前面用的方法不一样,不过是对的,写起来更快、
- return ans;
- }
- ll CR(int a[],int m[],int n)
- {
- ll M = 1;
- for (int i = 1;i <= n;++i) M*=m[i];
- ll ans = 0;
- for (int i = 1;i <= n;++i)
- {
- ll Mi = M/m[i];ll x,y;
- ll t = e_gcd(m[i],Mi,x,y);
- ans = (ans + y*Mi*a[i])%M;
- }
- return (M+ans%M)%M;
- }
=-=这个是我最初的模板,当然他非常渣,在经历了WA无数之后,模板得到了很好的改进,
- #include<iostream>
- #include<algorithm>
- #include<cstdio>
- #include<cstring>
- #include<cmath>
- #define mem(a,x) memset(a,x,sizeof(a))
- using namespace std;
- typedef long long ll;
- ll e_gcd (ll a, ll b, ll& x, ll& y)
- {
- if (b == 0)
- {
- x = 1, y = 0;
- return a;
- }
- ll ans = e_gcd (b, a % b, y, x);
- y -= a / b * x;
- return ans;
- }
- ll CR (int a[], int m[], int n)
- {
- ll Mi = m[1], ans = a[1];
- for (int i = 2; i <= n; ++i)
- {
- ll mi = m[i], ai = a[i];
- ll x, y;
- ll gcd = e_gcd (Mi, mi, x, y);
- ll c = ai - ans;
- if (c % gcd != 0) return -1;
- ll M = mi / gcd;
- ans += Mi * ( ( (c /gcd*x) % M + M) % M);
- Mi *= M;
- }
- if (ans == 0) //当余数都为0
- {
- ans = 1;
- for (int i = 1; i <= n; ++i)
- {
- ans = ans*m[i]/__gcd(ans,(ll)m[i]);
- }
- }
- return ans;
- }
- int main()
- {
- int T;cin>>T;int kas = 0;
- while (T--)
- {
- int n,a[100],m[100];
- scanf("%d",&n);
- for (int i = 1;i <= n;++i) scanf("%d",m+i);
- for (int i = 1;i <= n;++i) scanf("%d",a+i);
- printf("Case %d: %lld\n",++kas,CR(a,m,n));
- }
- return 0;
- }
不爽再来一题? Biorhythms
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<stdlib.h>
- #include<cmath>
- #include<algorithm>
- #define mem(a,x) memset(a,x,sizeof(a))
- using namespace std;
- typedef long long ll;
- ll e_gcd(ll a,ll b,ll &x,ll &y)
- {
- if (b == 0)
- {
- x = 1,y = 0;
- return a;
- }
- ll ans = e_gcd(b,a%b,y,x);
- y -= a/b*x;
- return ans;
- }
- ll CR(int a[],int m[],int n)
- {
- ll M = 1;
- for (int i = 1;i <= n;++i) M*=m[i];
- ll ans = 0;
- for (int i = 1;i <= n;++i)
- {
- ll Mi = M/m[i]; ll x,y;
- ll t = e_gcd(m[i],Mi,x,y);
- ans = (ans+y*Mi*a[i])%M;
- }
- ans = (M+ans%M)%M;
- if (ans == 0) //当余数都为0
- {
- ans = 1;
- for (int i = 1; i <= n; ++i)
- {
- ans = ans*m[i]/__gcd(ans,(ll)m[i]);
- }
- }
- return ans;
- }
- int main()
- {
- int a[4];
- int m[4] = {0,23,28,33};int d;int kas = 0;
- while (~scanf("%d %d %d %d",a+1,a+2,a+3,&d))
- {
- if (a[1]==-1&&a[2]==-1&&a[3]==-1&&d==-1) break;
- for (int i = 1;i <= 3;++i)
- {
- a[i] = a[i] - d;
- while (a[i]<0) a[i]+=m[i];
- }
- ll ans = CR(a,m,3);
- printf("Case %d: the next triple peak occurs in %lld days.\n",++kas,ans);
- }
- return 0;
- }
四、素数筛法
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- #define mem(a,x) memset(a,x,sizeof(a))
- #define inf (1<<29)
- using namespace std;
- typedef long long ll;
- const int N = 100;
- bool p[N+5];
- void init()
- {
- mem(p,1);
- for (int i = 2;i*i <= N;++i)
- {
- if (p[i])
- {
- for (int j = i*i;j <= N;j+=i)
- {
- p[j] = 0;
- }
- }
- }
- }
- int main()
- {
- init();
- for (int i = 2;i <= N;++i)
- {
- if (p[i]) cout<<i<<endl;
- }
- return 0;
- }
- #include<iostream>
- #include<cmath>
- #include<cstdio>
- #define mem(a,x) memset(a,x,sizeof(a))
- #define inf (1<<29)
- using namespace std;
- typedef long long ll;
- const int N = 100;
- int p[N+5];
- void init()
- {
- int sq = (int)sqrt(N*2+1);
- for (int i = 3;i <= sq;i+=2)
- {
- if (p[i>>1]) continue;
- for (int j = i*i;j <= N<<1;j+=i<<1)
- {
- p[j>>1] = 1;
- }
- }
- }
- int main()
- {
- init();
- puts("2");
- for (int i = 1;i < N;++i)
- {
- if (p[i]) continue;
- printf("%d\n",(i<<1)+1);
- }
- return 0;
- }
素数筛法三:
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- #define mem(a,x) memset(a,x,sizeof(a))
- #define inf (1<<29)
- using namespace std;
- typedef long long ll;
- const int N = 100000;
- int a[N+5];
- int p[N+5];
- void init()
- {
- int t = 0;
- for (int i = 2;i <= N;++i)
- {
- if (a[i] == 0)
- {
- p[++t] = i;
- }
- for (int j = 1,k;(j<=t)&&(k=i*p[j])<=N;++j)
- {
- a[k] = 1;
- if (i%p[j]==0) break;
- }
- }
- }
- int main()
- {
- init();
- for (int i = 1;p[i]>1;++i)
- {
- printf("%d\n",p[i]);
- }
- return 0;
- }
本来素数筛完应该直接上欧拉函数,不过觉得大素数测定蛮好玩的,于是插一句大素数测定与整数分解
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<string>
- #include<algorithm>
- #include<queue>
- #include<cmath>
- #include<stdlib.h>
- #include<cctype>
- #include<time.h>
- #define mem(a,x) memset(a,x,sizeof(a))
- using namespace std;
- typedef long long ll;
- const int S = 8;//测试次数
- ll mult_mod (ll a,ll b, ll c)
- {
- a%=c,b%=c;
- ll ret = 0;
- ll tmp = a;
- while (b)
- {
- if (b&1)
- {
- ret += tmp;
- if (ret > c) ret -= c;
- }
- tmp<<=1;
- if (tmp>c) tmp-=c;
- b>>=1;
- }
- return ret;
- }
- ll pow_mod(ll a,ll n,ll mod)
- {
- ll ret = 1;
- ll temp = a%mod;
- while (n)
- {
- if (n&1) ret = mult_mod(ret,temp,mod);
- temp = mult_mod(temp,temp,mod);
- n>>=1;
- }
- return ret;
- }
- bool check(ll a,ll n,ll x,ll t)
- {
- ll ret = pow_mod(a,x,n);
- ll last = ret;
- for (int i = 1;i <= t;++i)
- {
- ret = mult_mod(ret,ret,n);
- if (ret == 1&&last!=1&&last!=n-1) return 1;
- last = ret;
- }
- if (ret != 1) return 1;
- else return 0;
- }
- bool MR(ll n)
- {
- if(n < 2) return 0;
- if (n == 2) return 1;
- if ((n&1)==0) return 0;
- ll x = n - 1;
- ll t = 0;
- while ((x&1)==0)
- {
- x>>=1;++t;
- }
- srand(time(NULL));
- for (int i = 0;i < S;++i)//做S次测试
- {
- ll a = rand()%(n-1) + 1;
- if (check(a,n,x,t)) return 0;//只要其中有一次判定是合数就可以确定一定是合数
- }
- return 1;
- }
- int main()
- {
- ll n;
- while (cin>>n)
- {
- if (MR(n)) puts("YES");
- else puts("NO");
- }
- return 0;
- }
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- #include<time.h>
- #include<iostream>
- #include<algorithm>
- using namespace std;
- //****************************************************************
- // Miller_Rabin 算法进行素数测试
- //速度快,而且可以判断 <2^63的数
- //****************************************************************
- const int S=20;//随机算法判定次数,S越大,判错概率越小
- //计算 (a*b)%c. a,b都是long long的数,直接相乘可能溢出的
- // a,b,c <2^63
- long long mult_mod(long long a,long long b,long long c)
- {
- a%=c;
- b%=c;
- long long ret=0;
- while(b)
- {
- if(b&1){ret+=a;ret%=c;}
- a<<=1;
- if(a>=c)a%=c;
- b>>=1;
- }
- return ret;
- }
- //计算 x^n %c
- long long pow_mod(long long x,long long n,long long mod)//x^n%c
- {
- if(n==1)return x%mod;
- x%=mod;
- long long tmp=x;
- long long ret=1;
- while(n)
- {
- if(n&1) ret=mult_mod(ret,tmp,mod);
- tmp=mult_mod(tmp,tmp,mod);
- n>>=1;
- }
- return ret;
- }
- //以a为基,n-1=x*2^t a^(n-1)=1(mod n) 验证n是不是合数
- //一定是合数返回true,不一定返回false
- bool check(long long a,long long n,long long x,long long t)
- {
- long long ret=pow_mod(a,x,n);
- long long last=ret;
- for(int i=1;i<=t;i++)
- {
- ret=mult_mod(ret,ret,n);
- if(ret==1&&last!=1&&last!=n-1) return true;//合数
- last=ret;
- }
- if(ret!=1) return true;
- return false;
- }
- // Miller_Rabin()算法素数判定
- //是素数返回true.(可能是伪素数,但概率极小)
- //合数返回false;
- bool Miller_Rabin(long long n)
- {
- if(n<2)return false;
- if(n==2)return true;
- if((n&1)==0) return false;//偶数
- long long x=n-1;
- long long t=0;
- while((x&1)==0){x>>=1;t++;}
- for(int i=0;i<S;i++)
- {
- long long a=rand()%(n-1)+1;//rand()需要stdlib.h头文件
- if(check(a,n,x,t))
- return false;//合数
- }
- return true;
- }
- //************************************************
- //pollard_rho 算法进行质因数分解
- //************************************************
- long long factor[100];//质因数分解结果(刚返回时是无序的)
- int tol;//质因数的个数。数组小标从0开始
- long long gcd(long long a,long long b)
- {
- if(a==0)return 1;//???????
- if(a<0) return gcd(-a,b);
- while(b)
- {
- long long t=a%b;
- a=b;
- b=t;
- }
- return a;
- }
- long long Pollard_rho(long long x,long long c)
- {
- long long i=1,k=2;
- long long x0=rand()%x;
- long long y=x0;
- while(1)
- {
- i++;
- x0=(mult_mod(x0,x0,x)+c)%x;
- long long d=gcd(y-x0,x);
- if(d!=1&&d!=x) return d;
- if(y==x0) return x;
- if(i==k){y=x0;k+=k;}
- }
- }
- //对n进行素因子分解
- void findfac(long long n)
- {
- if(Miller_Rabin(n))//素数
- {
- factor[tol++]=n;
- return;
- }
- long long p=n;
- while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);
- findfac(p);
- findfac(n/p);
- }
- int main()
- {
- //srand(time(NULL));//需要time.h头文件//POJ上G++不能加这句话
- long long n;
- while(scanf("%I64d",&n)!=EOF)
- {
- tol=0;
- findfac(n);
- for(int i=0;i<tol;i++)printf("%I64d ",factor[i]);
- printf("\n");
- if(Miller_Rabin(n))printf("Yes\n");
- else printf("No\n");
- }
- return 0;
- }
整数分解也有个题: GCD & LCM Inverse(用dfs去找答案)
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<string>
- #include<algorithm>
- #include<queue>
- #include<cmath>
- #include<stdlib.h>
- #include<cctype>
- #define mem(a,x) memset(a,x,sizeof(a))
- using namespace std;
- typedef unsigned long long ll;
- const int S = 20;
- ll mult_mod (ll a, ll b, ll c)
- {
- a %= c, b %= c;
- ll ret = 0;
- ll tmp = a;
- while (b)
- {
- if (b & 1)
- {
- ret += tmp;
- if (ret > c) ret -= c;
- }
- tmp <<= 1;
- if (tmp > c) tmp -= c;
- b >>= 1;
- }
- return ret;
- }
- ll pow_mod (ll a, ll n, ll mod)
- {
- ll ret = 1;
- ll temp = a % mod;
- while (n)
- {
- if (n & 1) ret = mult_mod (ret, temp, mod);
- temp = mult_mod (temp, temp, mod);
- n >>= 1;
- }
- return ret;
- }
- bool check (ll a, ll n, ll x, ll t)
- {
- ll ret = pow_mod (a, x, n);
- ll last = ret;
- for (int i = 1; i <= t; ++i)
- {
- ret = mult_mod (ret, ret, n);
- if (ret == 1 && last != 1 && last != n - 1) return 1;
- last = ret;
- }
- if (ret != 1) return 1;
- else return 0;
- }
- bool MR (ll n)
- {
- if (n < 2) return 0;
- if (n == 2) return 1;
- if ( (n & 1) == 0) return 0;
- ll x = n - 1;
- ll t = 0;
- while ( (x & 1) == 0)
- {
- x >>= 1;
- ++t;
- }
- // srand(time(NULL));
- for (int i = 0; i < S; ++i) //做S次测试
- {
- ll a = rand() % (n - 1) + 1;
- if (check (a, n, x, t) ) return 0; //只要其中有一次判定是合数就可以确定一定是合数
- }
- return 1;
- }
- ll fac[11111];//质因数分解结果(刚返回时时无序的)
- int tot;//质因数的个数,数组下标从0开始
- ll gcd (ll a, ll b)
- {
- if (a == 0) return 1;
- if (a < 0) return gcd (-a, b);
- while (b)
- {
- ll t = a % b;
- a = b, b = t;
- }
- return a;
- }
- ll PR (ll x, ll c)
- {
- ll i = 1, k = 2;
- ll x0 = rand() % x;
- ll y = x0;
- while (1) //美丽的循环,不会死
- {
- ++i;
- x0 = (mult_mod (x0, x0, x) + c) % x;
- ll d = gcd (y - x0, x);
- if (d != 1 && d != x) return d;
- if (y == x0) return x;
- if (i == k)
- {
- y = x0;
- k += k;
- }
- }
- }
- void findfac (ll n) //对n进行素因子分解
- {
- if (MR (n) ) //如果n是素数
- {
- fac[tot++] = n;
- return;
- }
- ll p = n;
- while (p >= n) p = PR (p, rand() % (n - 1) + 1);
- findfac (p);
- findfac (n / p);
- }
- ll x[111];
- ll k;
- ll a, b, mn;
- ll ans;
- ll g, lcm;
- const ll inf = 1LL << 62LL;
- bool fd;
- void dfs (ll cur, ll p)
- {
- ll q = ans / p;
- if (gcd (p, q) == 1)
- {
- fd = 1;
- ll tmp = p * g + q * g;
- if (mn > tmp)
- {
- mn = tmp;
- a = p*g;
- b = q*g;
- }
- }
- if (cur > k) return;
- dfs(cur+1,p);p*=x[cur];
- if (p > mn ) return ;
- dfs(cur+1,p);
- }
- int main()
- {
- while (~scanf ("%llu %llu", &g, &lcm) )
- {
- if (g == lcm)
- {
- printf("%llu %llu\n",g,lcm);
- continue;
- }
- ans = lcm / g;
- tot = 0;
- // cout << ans << endl;
- findfac (ans);
- sort (fac, fac + tot); //fac保存的是ans的素因子,比如3 60对应的fac数组是2 2 5
- // for (int i = 0; i < tot; ++i) cout << fac[i] << " ";
- // cout << "---------------------------------------------------" << endl;
- k = 0;
- x[0] = fac[0];
- for (int i = 1; i < tot; ++i)
- {
- if (fac[i] == fac[i - 1]) x[k] *= fac[i];
- else x[++k] = fac[i];
- }
- sort (x, x + k + 1); //x保存的是所有不相同的素因子,比如3 60 对应的x数组是4 5
- // for (int i = 0; i <= k; ++i) cout << x[i] << " ";
- // cout << endl;
- //用dfs将数组x分成2部分p*q,满足p,q互质,找到所有p,q中使a+b最小的情况,其中a = p*lcm,b = q*lcm
- //比如3 60 ans = 20 4 5 ,a = 4*3 b = 5*3
- mn = inf;
- fd = 0;
- dfs (0, 1);
- if (a > b) swap (a, b);
- // if (!fd) puts ("???");
- printf ("%llu %llu\n", a, b);
- }
- return 0;
- }
- /*
- 7 635040
- */
- #include<iostream>
- #include<cmath>
- #include<algorithm>
- using namespace std;
- const int MAXN = 1e5;
- int isprime[MAXN];
- int prime[MAXN];
- int cnt;
- void getP()
- {
- cnt = 0;
- for(int i = 1; i < MAXN; i++)
- isprime[i] = 1;
- for(int i = 2; i < MAXN; i++)
- {
- if(!isprime[i])continue;
- prime[cnt++] = i;
- for(int j = 2 * i; j < MAXN; j += i)
- {
- isprime[j] = 0;
- }
- }
- }
- int euler( int n ) //求小于n且与n互质的数的个数
- {
- int ans = n;
- for(int i = 0; prime[i] * prime[i] <= n; i++)
- {
- if(n%prime[i] == 0)
- {
- ans = ans - ans / prime[i]; //ans=ans*(1-1/pi)
- while(n%prime[i] == 0)
- {
- n /= prime[i];
- }
- }
- }
- if(n > 1) ans = ans - ans / n;
- return ans;
- }
- int main()
- {
- getP();
- int n;
- while(cin >> n&&n)
- {
- cout << euler( n ) << endl;
- }
- return 0;
- }