题目链接: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
Recommend
chendu
【题意】:第一行输入样例组数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 <bits/stdc++.h>
#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 <bits/stdc++.h>
#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;
}