hdu多校赛第四场1010

开头先来说一下昨天写这道题的心路历程——非常的曲折。

(好像每一次打多校赛开的第一道题都是比较难的题)这次也不例外,非常可怜地开到了一道包含

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));
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值