NOIP复习模拟赛day5

前言 

今天的题目真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 }
View Code

自己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
",sum);  
19     }  
20 }  
View Code

代码...(推了一个下午)   

 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++
View Code
原文地址:https://www.cnblogs.com/to-the-end/p/9923052.html