HDU-4910 Problem about GCD

还是先来题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4910

这道题的题意就是给你个数n,让你把从1->n中与n互质的数找出来,并相乘,最后在模 n,输出。

这道题的题意可以说是非常暴力了, 不过暴力是不可能过的。我开始想着是可能有什么数学公式或规律,就开始推,果不其然,暴力打表之后就会发现答案要么是1, 要么是n-1. 再进一步观察,发现质数均为n-1,4的倍数(不包括4)均为1,再进一步观察,是单一质数的k次方的数也是n-1. 所以, 我们不妨可以猜想,除了4的倍数这种特例外,答案与n的质因子的种类数目有关。我们这里可以分奇偶来看一下,对于奇数而言,只要它们的质因子的种类数>1, 答案就是1, 而对于偶数而言,质因子种类数>2,答案为1,不过这里我们将偶数/2后就跟奇数状况相同了。于是,这道题的规律就被我们摸清楚了。

鉴于此题规律,首先想到的就是Miller_Rabbin + Pollard_Rho,不过直接TLE了,所以我们只好修改一下PR了,我们直接自己写数的分解,只要质因子种类数>1直接return就好了,不用再跑了。

AC代码:

#include <iostream>  
#include <cstdio>   
#include <cstring>  
#include <cmath>    
#include <cstdlib>  
typedef long long ll;   
const int maxn = 1000005;  
   
using namespace std;  
  
const int S=20;  
int prime[maxn];  
int c;  
bool vis[maxn];  
void init()  
{  
    c = 0;  
    memset(vis, 0, sizeof(vis));
    for(int i = 2;i <= 1000000; i++){  
        if(vis[i]) continue;  
        prime[c++] = i;  
        for(int j = i + i;j <= 1000000; j += i) vis[j] = true;  
    }  
}  
  
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;  
}  
long long pow_mod(long long x,long long n,long long mod)
{  
    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;  
}  
  
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;  
}  
  
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;
        if(check(a,n,x,t))  
            return false;
    }  
    return true;  
}  
bool squre(ll n)  
{  
    ll m = sqrt(n * 1.0);  
    return m * m == n || (m-1) * (m-1) == n || (m+1) * (m+1) == n;  
}  
bool solve(ll n)  
{  
    if(Miller_Rabin(n)) return true;
    int times = 0;  
    ll m = n;  
    for(int i = 0;i < c; i++){  
        if(m % prime[i] == 0){  
            times++; 
            if(times >= 2) return false;  
            while(m % prime[i] == 0)  {   
                m /= prime[i];  
            }  
        }  
        if(m == 1) return true; 
    }  
    if(m != n) return false;  //当m != n时,但m != 1,说明n至少有一个小于1e6和一个大于1e6的质因数,直接返回 
    if(squre(n)) return true;	/*若10^6以内的质因数都不能分解,就判断是不是完全平方数,因为10^18最多  
                              容许两个大于10^6的数相乘 */ 
    return false;  
}  
ll n;  
int main()  
{  
    init();   
    while(~scanf("%lld",&n)){  
        if(n == -1) break;  
        if(n == 1){  
            cout << "0" << '\n';
            continue;  
        }  
        if(n != 4 && n % 4 == 0){  
            cout << "1" << '\n';
            continue;  
        }  
        ll m = n;  
        if(m % 2 == 0) m /= 2;  
        if(!solve(m))  cout << "1" << '\n'; 
        else  cout << n-1 << '\n';
    }  
    return 0;  
}  
这个题的规律真难找,mmp.


猜你喜欢

转载自blog.csdn.net/water_zero_saber/article/details/79980765