还是先来题目链接: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.