前言
今天的题目真TM的♂......
题目
1.小半
(half.pas/c/cpp)
【问题描述】
“释然、慵懒、尽欢,
时间风干后你与我再无关,
没答案,怎么办,看不惯自我欺瞒。”
灯火阑珊,释然的少年写下了n个正整数,它们的乘积为p。
月色旖旎,少年忽然想到,如果把p再乘上一个正整数q能让它们的积为某个数的阶乘,则称这个数的阶乘为完美阶乘,这个数则为完美数,他厌倦了那些纷扰,只想要知道完美数的最小值,希望你能告诉他。
“灯火阑珊,
我的心借了你的光是明是暗。 ”——《小半》
【输入】
共两行。
第一行一个正整数n。
第二行n个正整数a[i],代表少年写下的n个数。
【输出】
共一行
一个正整数,代表完美数的最小值。
【输入输出样例】
|
1 6 |
3 |
样例解释:当p=6,q=1时,p*q=3!
【数据范围】
对于10%的数据,n<=10
对于30%的数据,n<=1000
对于100%的数据,n<=100000,a[i]<=100000
官方题解:
10%~30%:各种暴力
100%:题目要求一个最小的m使m!包含p这个因子。
可以把p分解质因数,假设p=∏ai^bi(ai为质数),那么只要m!包含了每个ai^bi,m!就包含p。
所以对于每个ai^bi,分别求出满足条件的最小的m,取最大值即可。
怎么求m?
先看一个简单的问题:
27!里面有多少个3相乘?
27!=1*2*...*27
包含1个3的数有27/(3^1)=9个
包含2个3的数有27/(3^2)=3个
包含3个3的数有27/(3^3)=1个
总共:9+3+1=13个
所以27!里面有13个3相乘。
用这个方法就可以求得m!有多少个ai相乘,二分判断即可。
这是一道水题,相信大家都切了o(* ̄︶ ̄*)o
官方标程:

1 #include<cstdio>
2 #include<cstring>
3 #include<iostream>
4 #include<algorithm>
5 #define fo(i,a,b) for(int i=a;i<=b;i++)
6 #define fd(i,a,b) for(int i=a;i>=b;i--)
7 #define maxn 100005
8 #define ll long long
9 using namespace std;
10
11 int bz[maxn],pri[maxn],fr[maxn];
12
13 int cnt[maxn];
14
15 int a[maxn][2],tot;
16
17 int n;
18
19 void pre(){
20 fo(i,2,100000) {
21 if (bz[i]==0) pri[++pri[0]]=i,fr[i]=i;
22 fo(j,1,pri[0]) {
23 if (i * pri[j]>100000) break;
24 bz[i*pri[j]]=1;
25 fr[i*pri[j]]=pri[j];
26 if (i % pri[j]==0) break;
27 }
28 }
29 }
30
31 void pred(){
32 int x;
33 scanf("%d",&x);
34 while (x!=1) {
35 cnt[fr[x]]++;
36 x=x / fr[x];
37 }
38 }
39
40 ll count(ll x,ll y){
41 ll ret=0;
42 while (x>=y) {
43 ret=ret+x / y;
44 x=x / y;
45 }
46 return ret;
47 }
48
49 bool check(ll x) {
50 fo(i,1,tot) if (count(x,a[i][0])<a[i][1]) return 0;
51 return 1;
52 }
53
54 int main(){
55 pre();
56 scanf("%d",&n);
57 fo(i,1,n) pred();
58 fo(i,1,100000) if (cnt[i]) {
59 ++tot;
60 a[tot][0]=i;
61 a[tot][1]=cnt[i];
62 }
63 if (tot==0) {
64 cout<<1;
65 return 0;
66 }
67 ll ans=0,l=2,r=(ll)1e10;
68 while (l<=r) {
69 ll mid=(l+r) / 2;
70 if (check(mid)) {
71 r=mid-1;
72 ans=mid;
73 }
74 else l=mid+1;
75 }
76 cout<<ans;
77 return 0;
78 }
自己bb的题解:
首先,这一题是真的♂,我打了一个早上,才推出了一半。(我还是太菜了)
这题首先我们要打一个欧拉筛,来筛出1~100000内的质数;
然后我们要把每个A分解质因数;
然后二分1~100000的完美数,找出这个阶乘里所含的质因子的个数,与我们分解的A的质因数进行比较,如果大于等于A的质因数就继续往左区间寻找答案,否则反之...
如何寻找m!分解后n因子的个数???
给定两个数m,n
求m!分解质因数后因子n的个数。
这道题涉及到了大数问题,如果相乘直接求的话会超出数据类型的范围。
下面给出一种效率比较高的算法,我们一步一步来。
m!=1*2*3*……*(m-2)*(m-1)*m
可以表示成所有和n倍数有关的乘积再乘以其他和n没有关系的
=(n*2n*3n*......*kn)*ohter other是不含n因子的数的乘积 因为 kn<=m 而k肯定是最大值 所以k=m/n
=n^k*(1*2*......*k)*other
=n^k*k!*other
从这个表达式中可以提取出k个n,然后按照相同的方法循环下去可以求出k!中因子n的个数。
每次求出n的个数的和就是m!中因子n的总个数。

1 #include<stdio.h>
2 int main()
3 {
4 int t;
5 scanf("%d",&t);
6 while(t--)
7 {
8 int m,n;
9 scanf("%d%d",&m,&n);
10 long int sum=0;
11 while(1)
12 {
13 sum+=m/n;
14 m=m/n;
15 if(m==0)
16 break;
17 }
18 printf("%d\n",sum);
19 }
20 }
代码...(推了一个下午)

1 //NOIPRP++
2 #include<bits/stdc++.h>
3 #define Re register int
4 using namespace std;
5 int N,A,MinPrime[100005],Prime_Num[100005],Num[100005];
6 long long l=1,r=1e10,ans;
7 bool Isprime[100005];
8 inline void read(int &x){
9 x=0; char c=getchar(); bool p=1;
10 for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
11 for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
12 p?:x=-x;
13 }
14 inline void Check_Prime(){
15 memset(Isprime,true,sizeof(Isprime));
16 for (Re i=2;i<=100000;i++){
17 if (Isprime[i]) Prime_Num[++Prime_Num[0]]=MinPrime[i]=i;
18 for (Re j=1;j<=Prime_Num[0]&&i*Prime_Num[j]<=100000;j++){
19 Isprime[i*Prime_Num[j]]=false;
20 MinPrime[i*Prime_Num[j]]=Prime_Num[j];
21 if (i%Prime_Num[j]==0) break;
22 }
23 }
24 }
25 inline bool Check(long long K){
26 for (Re i=1;i<=Prime_Num[0];i++){
27 long long Tmp=K,Val=0;
28 while (Tmp){Val+=Tmp/Prime_Num[i];Tmp/=Prime_Num[i];}
29 if (Val<Num[Prime_Num[i]]) return false;
30 }
31 return true;
32 }
33 int main(){
34 Re i,j;
35 Check_Prime();
36 read(N);
37 for (i=1;i<=N;i++){
38 read(A);
39 while (A^1){++Num[MinPrime[A]];A/=MinPrime[A];}
40 }
41 while (l<=r){
42 long long Mid=(l+r)>>1;
43 if (Check(Mid)) ans=Mid,r=Mid-1;
44 else l=Mid+1;
45 }
46 printf("%lld",ans);
47 return 0;
48 }
49 //NOIPRP++
来源:https://www.cnblogs.com/to-the-end/p/9923052.html
