Problem about GCD
Given integer m. Find multiplication of all 1<=a<=m such gcd(a, m)=1 (coprime with m) modulo m.
Input
Input contains multiple tests, one test per line.
Last line contains -1, it should be skipped.
[Technical Specification]
m <= 10^18
Output
For each test please output result. One case per line. Less than 160 test cases.
Sample Input
1
2
3
4
5
-1
Sample Output
0
1
2
3
4
题意:
给定一个数N(N≤1018)(N≤1018)求出N以内的和N互质的数的乘积再模N
分析:
这道题通过打表可以发现规律
而这实际上数论中的一个结论:
N为1,2,4,以及只有一个质因子(奇数)或者它的1212只有一个质因子(偶数),答案是N-1.其余情况答案均是 1。
因为N很大,所以不可能将质因子全部分解,我们发现对于一个数N它的质因子最多只能有2个大于1e6的,因为3个1e6的质因子相乘就等于1e18了,故根据这个性质,我们只需要筛选出1e6范围内的素数即可
然后将N分解,如果有多于1种质因子则答案为1,而分解完之后剩下的数只有一下四种情况
1)为1,并且素因子种类数为1
2)为1,并且素因子种类数大于1
3)大于1,素因子种类数大于1了
4)大于1,种类数恰好一种这说明这个数本身可能是素数,而判断他是素数因为数太大我们只能使用大素数测试的方法即(拉宾米勒的合数测试),而如果不是素数还有一种可能情况,就是它是两个大于1e6的素因子的乘积,因此我们看一下是不是完全平方数即可(因为最多两个这样的素因子)
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
const int S = 8;
ll x;
ll prime[maxn],isprime[maxn],cnt;
void init(){
cnt = 0;
memset(isprime,true,sizeof(isprime));
isprime[0] = isprime[1] = false;
for(int i = 2 ; i < maxn; i++){
if(isprime[i]){
prime[cnt++] = i;
for(int j = i+i; j < maxn; j += i){
isprime[j] = false;
}
}
}
}
ll mul(ll a,ll b,ll mod){
ll ans = 0;
while(b){
if(b & 1)
ans = (ans + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return ans;
}
ll q_pow(ll a,ll b,ll mod){
ll ans = 1;
while(b){
if(b & 1)
ans = mul(ans,a,mod);
a = mul(a,a,mod);
b >>= 1;
}
return ans;
}
bool check(ll a,ll n,ll x,ll t){
ll ret = q_pow(a,x,n);
ll last = ret;
for(int i = 1; i <= t; i++){
ret = mul(ret,ret,n);
if(ret == 1 && last != 1 && last != n-1) return true;
last = ret;
}
if(ret != 1) return true;
return false;
}
bool Miller_Rabin(ll n){
if(n < 2) return false;
if(n == 2) return true;
if((n & 1) == 0) return false;
ll x = n - 1;
ll t = 0;
while((x & 1) == 0){
x >>= 1;
t++;
}
for(int i = 0; i < S; i++){
ll a = rand() % (n - 1) + 1;
if(check(a,n,x,t))
return false;
}
return true;
}
bool judge(ll x){
ll num = 0;
for(int i = 1; prime[i] <= x && i < cnt; i++){
if(x % prime[i] == 0){
num++;
while(x % prime[i] == 0) x /= prime[i];
if(num > 1) return false;//素因子种类大于1返回false
}
if(x == 1) break;
}
if(x > 1) num++;//如果剩余大于1种类+1
if(num > 1) return false;//加上最后一个数素因子种类大于1了返回false
if(Miller_Rabin(x)) return true;//如果种类数为1,判断是不是素数是就返回true
ll m = (ll)sqrt(x*1.0);//否则再看是否是完全平方数,因为这样素因子只有一种而且大于1e6的素因子最多两个
if(m * m == x) return true;
return false;//其他只能返回false如两个不同的大于1e6的素因子就不行
}
int main(){
init();
while(scanf("%lld",&x) != EOF){
if(x == -1) break;
if(x != 4 && x % 4 == 0){
printf("1\n");
continue;
}
if(x == 1 || x == 2 || x == 4){
printf("%lld\n",x-1);
continue;
}
if(x & 1){//奇数直接判断
if(judge(x))
printf("%lld\n",x-1);
else
printf("1\n");
}
else{//偶数除以2后再判断
if(judge(x/2))
printf("%lld\n",x-1);
else
printf("1\n");
}
}
return 0;
}