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 }
自己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++