阶乘
阶乘
https://ac.nowcoder.com/acm/contest/5505/C
Solution
题面简洁明了,(就喜欢这种题面)。首先我们需要知道的前导知识,任何一个数,都可以分解为一些素数的幂之积,又叫唯一分解定理,那么对于阶乘这样一个乘积运算,自然也可以进行多次分解质因数。写成素数幂之积。这里给出阶乘求解质因数幂的算法
ll factory(ll n, ll s) { //n!对素数s的求解 ll sum = 0; while (n) { n /= s; sum += n; } return sum; //返回幂 }
那么知道了分解质因数之后,首先如果当前答案的阶乘是p的倍数,就说明每一个素数幂大于等于p的对应素数幂,并且这个答案我们希望再小一点,如果当前答案阶乘不是p的倍数,就说明存在一个素数的幂小于p对应素数幂,并且这个答案一定要比求解答案小。符合单调,选择二分。
知道这样两个知识之后这题也就解决了,只存在把这些函数接口接好。
1、对p分解质因数求解到对应素数与对应幂
2、二分答案,对答案也分解质因数,与p的幂对比
Code
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1000; ll prime_num[maxn], num;//质因数的个数 ll prime[maxn]; void divid(ll n) { num = 0; ll i, term; for (int i = 2; i * i <= n; ++i) if (n % i == 0) { term = 0; while (n % i == 0) { ++term; n /= i; } prime_num[num] = term; prime[num++] = i; } if (n > 1) { prime[num] = n; prime_num[num++] = 1; } } ll factory(ll n, ll s) { ll sum = 0; while (n) { n /= s; sum += n; } return sum; } bool check(ll x) { for (int i = 0; i < num; ++i) if (factory(x, prime[i]) < prime_num[i]) return false; return true; } int main() { int t; scanf("%d", &t); while (t--) { ll x; scanf("%lld", &x); if (x <= 2) { printf("%lld\n", x); continue; } divid(x); ll l = 1, r = 1e16, ans; while (l <= r) { ll mid = l + ((r - l) >> 1); if (check(mid)) { ans = mid; r = mid - 1; } else l = mid + 1; } printf("%lld\n", ans); } return 0; }
!!这里突然发现个更短时间的代码,上面代码跑了457MS,下面跑了144MS。想想也是,毕竟没有二分,掉了这里log的复杂度把,这里说一句不愧是杨大佬,不愧是这次周练第二的男人,tql!!直接跑质因数的幂结果大于等于p的当前素数幂,并计算符合要求的最小次幂的计算结果,并把答案更新。
#include<bits/stdc++.h> #define js ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) using namespace std; int main() { int t,n,i,j,ans=0; js; cin>>t; while(t--) { cin>>n; ans=0; for(i=2;i*i<=n;++i) if(n%i==0) { //i是n的一个因子 int cnt=0; while(n%i==0) { //计算i的幂 n/=i; ++cnt; } int tmp=0; for(j=i;;j+=i) { //找到符合要求的j的最小倍数 for(int p=j;p%i==0;p/=i,++tmp); if(tmp>=cnt) break; } ans=max(ans,j); } cout<<max(ans,n)<<endl; } }