2019杭电暑期多校赛-第4场-Minimal Power of Prime【分治】

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6623

Minimal Power of Prime

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1472    Accepted Submission(s): 320


Problem Description
You are given a positive integer n > 1. Consider all the different prime divisors of n. Each of them is included in the expansion n into prime factors in some degree. Required to find among the indicators of these powers is minimal.
 

Input
The first line of the input file is given a positive integer T ≤ 50000, number of positive integers n in the file. In the next T line sets these numbers themselves. It is guaranteed that each of them does not exceed 10^18.
 

Output
For each positive integer n from an input file output in a separate line a minimum degree of occurrence of a prime in the decomposition of n into simple factors.
 

Sample Input
    
    
    
    
5 2 12 108 36 65536
 

Sample Output
    
    
    
    
1 1 2 2 16
 

Source
2019 Multi-University Training Contest 4
 

Recommend
chendu
 

Statistic |  Submit |  Discuss | Note


【题意】:第一行输入样例组数T,接着T行,每行给一个2~1e18的正整数n,你要去求n分解质因数后,所有质因数中幂次最小的质因数(也就是出现次数最少的质因数),然后输出这个最小幂次(最少出现次数)。

【题解】: 我们首先用不超过N 1/5的质数去分解N(这样是为了把数据范围搞小,1e18太恐怖了),分解后剩下的N我们把它记做M,那么如果剩下的M = 1(也就是说N的所有质因数都小于等于N 1/5),那么直接把刚才分解过程中幂次最小的输出就可以了;如果M != 1,那么M一定只能分解成类似P、P*Q、P 2、P 3、P*Q 2、P 2*Q、P 4、P 2*Q 2这些个情况(P、Q为素数)。那么最后的ans肯定是min(前面分解过的质因数里面最小幂次,M中包含的最小幂次)。观察后会发现:
1.如果M 1/4是一个整数,也就是M = P 4,则ans = min(4, 之前的最小值); return;
2.如果M 1/3是一个整数,也就是M = P 3,则ans = min(3, 之前的最小值); return;
3.如果M 1/2是一个整数,也就是M = P 2或者M = P 2*Q 2,无论哪种情况,此时M中对应的最小质因数的幂次都是2,则ans = min(2, 之前的最小值); return;
4.显然如果以上都不满足,则M一定是P、P*Q、P*Q 2、P 2*Q中的一种,无论是哪一种,M对应的最小幂次都是1,那答案就直接输出“1”就可以了。
由于一共只需要跑O(N 1/5/log(N)) 个素数,所以最后总的期望运行的时间复杂度为O(T*N 1/5/log(N))。(T是样例组数)。

这一题一开始写Miller-Rabin加Pollard-Rho分解质因数,标记每次跑的素数,结果果然TLE……太菜了Orz……lyy大佬上来就刚这一题直接1A,其余全场TLE(暴风自闭)。
Miller-Rabin加Pollard-Rho分解质因数的TLE代码:
#include 
#define INF 0x3f3f3f3f
#define ll long long
#define LL long long
using namespace std;

LL Mult_Mod(LL a, LL b, LL m) { //res=(a*b)%m
    a %= m;
    b %= m;
    LL res = 0;
    while(b) {
        if(b & 1)
            res = (res + a) % m;
        a = (a <<= 1) % m;
        b >>= 1;
    }
    return res % m;
}
LL Pow_Mod(LL a, LL b, LL m) { //res=(a^b)%m
    LL res = 1;
    LL k = a;
    while(b) {
        if((b & 1))
            res = Mult_Mod(res, k, m) % m;
        k = Mult_Mod(k, k, m) % m;
        b >>= 1;
    }
    return res % m;
}

bool Witness(LL a, LL n, LL x, LL sum) {
    LL judge = Pow_Mod(a, x, n);
    if(judge == n - 1 || judge == 1)
        return 1;
    while(sum--) {
        judge = Mult_Mod(judge, judge, n);
        if(judge == n - 1)
            return 1;
    }
    return 0;
}

bool Miller_Rabin(LL n) {
    if(n < 2)
        return 0;
    if(n == 2)
        return 1;
    if((n & 1) == 0)
        return 0;

    LL x = n - 1;
    LL sum = 0;
    while(x % 2 == 0) {
        x >>= 1;
        sum++;
    }
    int times = 5;     /// 随机化次数
    for(LL i = 1; i <= times; i++) {
        LL a = rand() % (n - 1) + 1; //取与p互质的整数a
        if(!Witness(a, n, x, sum)) //费马小定理的随机数检验
            return 0;
    }
    return 1;
}
LL GCD(LL a, LL b) {
    return b == 0 ? a : GCD(b, a % b);
}
LL Pollard_Rho(LL n, LL c) { //寻找一个因子
    LL i = 1, k = 2;
    LL x = rand() % n; //产生随机数x0(并控制其范围在1 ~ x-1之间)
    LL y = x;
    while(1) {
        i++;
        x = (Mult_Mod(x, x, n) + c) % n;
        LL gcd = GCD(y - x, n);

        if(gcd < 0)
            gcd = -gcd;

        if(gcd > 1 && gcd < n)
            return gcd;

        if(y == x)
            return n;

        if(i == k) {
            y = x;
            k <<= 1;
        }
    }
}

unordered_map<ll, int> pcnt;
unordered_map<ll, bool> isp;
void Find_fac(LL n) { //对n进行素因子分解,存入factor
    if(isp[n] || Miller_Rabin(n)) { //是素数就把这个素因子存起来
        isp[n] = true;
        ++pcnt[n];
        return;
    }

    long long p = n;
    while(p >= n) //值变化,防止陷入死循环k
        p = Pollard_Rho(p, rand() % (n - 1) + 1);

    Find_fac(n / p);
    Find_fac(p);
}

int findmin() {
    int minn = INF;
    for(auto it : pcnt) {
        minn = min(minn, it.second);
    }
    return minn;
}

int main() {
    srand((unsigned)time(0));
    int T;
    cin >> T;
    while(T--) {
        pcnt.clear();
        ll t;
        cin >> t;
        if (Miller_Rabin(t))
            puts("1");
        else {
            Find_fac(t);
            cout << findmin() << endl;
        }
    }
    return 0;
}

AC代码(要注意开n次根号的问题,pow太玄学了。流同步关上后,cout不能和puts一起用):
#include 

#define INF 0x3f3f3f3f
#define EXP 1e-6
#define MEM(a,x) memset(a,x,sizeof(a))
#define gcd(a,b) __gcd(a,b)
#define ll long long
#define LL long long
#define _for(i,a,b) for(int i = (a); i < (b); i++)
#define _fore(i,a,b) for(int i = (a); i <= (b); i++)

using namespace std;

bool vst[5500];
int pri[550], cnt = 0;
unordered_map<int, int> less_cnt;

ll qpow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1)
            ans = ans * a;
        b >>= 1;
        a = a * a;
    }
    return ans;
}

double nsqrt(double x, int n) {
    if (x < 0 && !(n & 1))
        return -1;
    double ans = pow(x, 1.0 / n);
    if (abs(qpow((ll)ans + 1, n) - x) < EXP)
        return ans + 1;
    else
        return ans;
}

void sieve(int n) {	//线性筛筛出来5次根号1e18以内的素数
    pri[cnt++] = 2, vst[4] = true;
    for(int i = 3; i < n; i += 2) {
        if(!vst[i])
            pri[cnt++] = i;
        for(int j = 0; (j < cnt && i * pri[j] < n); ++j) {
            vst[i * pri[j]] = true;
            if(i % pri[j] == 0)
                break;
        }
    }
}

int findmin() {	//找到5次根号下1e18以内,最小的质因数
    int minn = INF;
    for(auto it : less_cnt) {
        minn = min(minn, it.second);
        if (minn == 1)	// 1必然是最小的,有1的话直接返回1就行了
            return 1;
    }
    return minn;
}

int main() {
    ios::sync_with_stdio(false);
    sieve(3982);	//3982是5次根号1e18
    int T;
    cin >> T;
    while (T--) {
        less_cnt.clear();
        ll n, m, sq5 = 0;
        cin >> n;
        m = n, sq5 = nsqrt(n, 5);
        _for(i, 0, cnt) {
            if (pri[i] > sq5)
                break;
            while (m % pri[i] == 0) {
                m /= pri[i];
                ++less_cnt[pri[i]];	//记录个数
            }
        }
        if (m == 1) {
            cout << findmin() << endl;
        } else {
            bool flag = false;
            int ans = findmin();
            if (ans == 1) {
                cout << 1 << endl;
                continue;
            }
            for(int i = 4; i >= 2; --i) {	//必须倒着来,仔细想想4次根号和2次根号的关系就知道了
                ll res = nsqrt(m, i);
                if (qpow(res, i) == m) {	// 这里跑了两遍快速幂其实可以优化,可我懒得改了
                    flag = true;
                    ans = min(ans, i);
                    break;
                }
            }
            if(!flag) {
                cout << 1 << endl;
            } else {
                cout << ans << endl;
            }
        }
    }
    return 0;
}

你可能感兴趣的:(ACM,C++)