开头先来说一下昨天写这道题的心路历程——非常的曲折。
(好像每一次打多校赛开的第一道题都是比较难的题)这次也不例外,非常可怜地开到了一道包含
10的18次方如此大数的(一定不能遍历题)说实话本蒟蒻5个小时都没有想出巧妙的解题办法。
一开始的(经过优化的小遍历)
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int c[maxn];
int p[maxn], m;
void divide(long long n){
m = 0;
double nn = n;
for(long long i = 2; i <= (long long)sqrt(nn)+1; i++){
if(n % i == 0){// i 是质数
p[++m] = i, c[m] = 0;
while(n % i == 0){
n /= i, c[m]++;
}
}
if(i>=3) i++;
}
if(n > 1){// n 是质数
p[++m]=n, c[m] = 1;
}
int min = c[1];
for(int i = 1;i <= m;i++){
if(min > c[i])
min = c[i];
}
printf("%d\n",min);
}
int main(){
int t;
long long n;
while(~scanf("%d",&t)){
for(int i = 0;i < t;i++){
scanf("%lld",&n);
divide(n);
}
}
}//10000001400000049
对于一个小白来说写出这样的代码是在正常不过了,因为十分普通。
在tle了几发之后(其实在写的时候就意识到一定会t)开始进入到对素数性质的数学的学习。
(然而并没有卵用)
经过一番挣扎之后码了一个米勒拉宾素数筛选法
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <time.h>
#define N 10100
typedef long long ll;
using namespace std;
const int S = 8; //随机判定次数 一般8 - 10
// a*b%c
ll mult_mod(ll a,ll b,ll c)
{
a %= c;
b %= c;
ll ret = 0;
ll temp = a;
while(b)
{
if(b&1)
{
ret += temp;
if(ret > c) ret -= c;
}
temp <<= 1;
if(temp > c) temp -= c;
b >>= 1;
}
return ret;
}
// (a^n)%mod
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;
}
//通过费马小定理,a^(n-1)=1(mod n) ;来判断n是否是素数
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 true;
last = ret;
}
if(ret != 1) return true;
else 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++;
};
srand(time(NULL)); //G++时不要
for(int i = 0; i < S; i++)
{
ll a = rand()%(n - 1) + 1;
if(check(a,n,x,t))
return false;
}
return true;
}
ll factor[100];
int tol;
ll gcd(ll a,ll b)
{
ll t;
while(b)
{
t = a;
a = b;
b = t % b;
}
if(a >= 0) return a;
else
return -a;
}
//找因子
ll pollard_rho(ll x,ll c)
{
ll i = 1,k = 2;
srand(time(NULL));
ll x1 = rand()%(x-1)+1;
ll y = x1;
while(1)
{
i++;
x1 = (mult_mod(x1,x1,x)+c)%x;
ll d = gcd(y-x1,x);
if(d!=1 && d!=x) return d;
if(y == x1) return x;
if(i == k)
{
y = x1;
k += k;
}
}
}
//对n进行分解,存入数组,
void findfac(ll n,int k) //大数分解
{
if(n == 1)
return ;
if(miller_rabin(n)) //判素
{
factor[tol++] = n;
return ;
}
ll p = n;
int c = k;
while( p>= n)
p = pollard_rho(p,c--); //防止死循环k,值变换
findfac(p,k);
findfac(n/p,k);
}
int main()
{
int t;
ll n;
scanf("%d",&t);
while(t--)
{
scanf("%I64d",&n);
if(miller_rabin(n)) printf("1\n");
else
{
tol = 0;
findfac(n,107);
sort(factor,factor+tol);
int minn=100;
for(int i=0;i<=tol-1;i++){
int ans=1;
//cout<<factor[i]<<' ';
while(factor[i]==factor[i+1]&&factor[i]!=0&&i<tol-1){
//cout << factor[i] << "- ";
ans++;
i++;
}
//cout <<'-'<< ans <<" ";
if(minn>ans)
minn=ans;
}
printf("%d\n",minn);
}
}
return 0;
}
经过手动测试 跑出答案的速度非常快
(然而也没有卵用)
怀着难以置信的心情tle了10来发
最终学会了——分组筛选法
先筛选出10的4次方以内的所有素数,那么题目中给出的n——10的18次方以内,最多就是10的4次方,那么我们就可以很快地找出答案
最后附上ac代码
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=4000;
int T,i,j,tot,v[N],p[N];ll n;
inline ll po(ll a,int b){
ll ret=1;
while(b--)ret*=a;
return ret;
}
inline bool check(ll n,int k){
ll t=pow(n,1.0/k);
t-=5;
if(t<1)t=1;
while(po(t+1,k)<=n)t++;
return po(t,k)==n;
}
inline void up(int&a,int b){if(a>b)a=b;}
inline int solve(ll n){
int ret=100,i;
for(i=0;i<tot&&p[i]<=n;i++)if(n%p[i]==0){
int t=0;
while(n%p[i]==0)t++,n/=p[i];
up(ret,t);
}
if(n==1)return ret;
for(i=4;i>=2;i--)if(check(n,i)){
up(ret,i);
return ret;
}
return 1;
}
int main(){
for(i=2;i<N;i++)if(!v[i]){
p[tot++]=i;
for(j=i;j<N;j+=i)v[j]=1;
}
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
printf("%d\n",solve(n));
}
}