1. 程式人生 > >【2018/10/05】T1

【2018/10/05】T1

我好菜啊……好菜啊……好菜啊……

階乘 

(fact)【問題描述】  有 n個正整數 a[i],設它們乘積為 p,你可以給 p乘上一個正整數 q,使 p*q剛好為正整數m的階乘,求m的最小值。 【輸入】  共兩行。  第一行一個正整數n。  第二行n個正整數a[i]。 【輸出】  共一行  一個正整數m。 【輸入樣例】  1  6 【輸出樣例】  3   樣例解釋:  當p=6,q=1時,p*q=3!    【資料範圍與約定】  對於10%的資料,n<=10  對於30%的資料,n<=1000  對於100%的資料,n<=100000,a[i]<=100000 

牢騷

我覺得我可能是把這道題想複雜了吧,反正我推了一個半小時,居然連一分都沒拿到手%>_<%,

思路離正解很近了,只是可能因為長時間耗在上面,思維固化了,就沒想出來程式碼該怎麼寫,以後還是別在一道題上吊死

主要還是不知道怎麼分解階乘的質因數

今天評講完後總算會了一點點了

先看一個簡單的問題:  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相乘。 

具體程式碼實現呢大概也就是用一個while迴圈不停的除,然後累加即可

分析

首先根據標題 fact,我們就能八九不離十,猜出來要分解質因數了

然後可以把p分解質因數,假設 p=∏ai^bi(ai為質數),那麼只要 m!包含了每個ai^bi,m!就包含p。  所以就可以二分答案咯,因為m!這個是單增的

程式碼  

#include<bits/stdc++.h>
#define N 100000
#define in read()
using namespace std;
int n;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
bool mark[N+10];
int pri[N+10],num=0;
void init(){//線性篩素數
	mark[1]=1;
	for(int i=2;i<=N;++i){
		if(!mark[i]) pri[++num]=i;
		for(int j=1;j<=num&&i*pri[j]<=N;++j){
			mark[i*pri[j]]=1;
			if(i%pri[j]==0) break;
		}
	}
}
int a,b[N],maxn=-1,c[N];
void fenjie(int x){//對p進行分解
	int tmp;
	for(int i=1;i<=num&&pri[i]<=x;++i){
		tmp=x;
		while(tmp%pri[i]==0){
			tmp/=pri[i];
			b[pri[i]]++;
			maxn=max(maxn,pri[i]);
		}
	}
	if(tmp)	b[tmp]++;
}
int check(int m){//二分答案的檢驗
	memset(c,0,sizeof(c));
	int tmp;
	for(int i=1;i<=num&&pri[i]<=m;++i){
		tmp=m;
		while(tmp){//就是這裡啦,階乘的分解質因數
			c[pri[i]]=(c[pri[i]]+tmp/pri[i]);
			tmp/=pri[i];
		}
	}
	for(int i=2;i<=maxn;++i)
		if(!mark[i]) if(b[i]>c[i]) return 0;
	return 1;
}
int main(){
	n=in;
	init();
	while(n--){
		a=in;
		fenjie(a);
	}
	int l=1,r=1000000000,ans=0;
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%d",ans);
	return 0;
}