天天看點

BZOJ 3000(Big Number-Stirling公式求n!近似值)

3000: Big Number

Time Limit: 2 Sec  Memory Limit: 128 MB

Submit: 220  Solved: 62

[ Submit][ Status]

Description

給你兩個整數N和K,要求你輸出N!的K進制的位數。

Input

有多組輸入資料,每組輸入資料各一行,每行兩個數——N,K

Output

       每行一個數為輸出結果。

Sample Input

2 5

2 10

10 10

100 200

Sample Output

1

1

7

69

對于100%的資料,有2≤N≤2^31, 2≤K≤200,資料組數T≤200。

HINT

Source

本題需要用到Stirling公式

BZOJ 3000(Big Number-Stirling公式求n!近似值)

∴位數=log10(n!)+1

方案1:[WA]n!不斷div k

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Lson (x<<1)
#define Rson ((x<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
long long mul(long long a,long long b){return (a*b)%F;}
long long add(long long a,long long b){return (a+b)%F;}
long long sub(long long a,long long b){return (a-b+(a-b)/F*F+F)%F;}
typedef long long ll;
typedef long double ld;
ld log(ll a,int b){}
int main()
{
//	freopen("bzoj3000.in","r",stdin);
//	freopen(".out","w",stdout);
	ll n;int k;
	while (scanf("%lld%d",&n,&k)!=EOF)
	{
		if (n<=10000)
		{
			ll ans=0,p=1;
			Fork(i,2,n) 
			{
				p*=i;
				while (p>=k) {p/=k,ans++;}
				cout<<i<<':'<<p<<' '<<ans<<endl;
			}
			if (p) ans++;
			printf("%lld\n",ans);
		}		
	}
	
	
	return 0;
}
           

但是這樣做是錯的。TNT

理由是不斷div k 會把原來小的部分删掉

方案2:logk(n!)=log(1)+log(2)+...+log(n)

效率為O(n),顯然也不行

于是不妨用Stirling公式求近似值

BZOJ 3000(Big Number-Stirling公式求n!近似值)

位數=logk(n!)+1

≈ logk(sqrt(2πn)*(n/e)^n+1

= logk(sqrt(2πn))+log[(n/e)^n]+1

=1/2*logk(2πn)+nlog(n/e)+1

=0.5*logk(2πn)+nlog(n/e)+1

   =0.5*logk(2πn)+nlog(n)-nlog(e)+1

PS:pi=acos(-1.0),e=exp(1)

PS2:eps的存在是為了防止n=2,k=2這樣剛好的情況出現,這個時候向上取整要多取1位

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Lson (x<<1)
#define Rson ((x<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
long long mul(long long a,long long b){return (a*b)%F;}
long long add(long long a,long long b){return (a+b)%F;}
long long sub(long long a,long long b){return (a-b+(a-b)/F*F+F)%F;}
typedef long long ll;
typedef long double ld;
ld const pi=acos(-1.0),e=exp(1),eps=1e-10;
ld log(ld a,ld b){return log(a)/log(b);}
int main()
{
//	freopen("bzoj3000.in","r",stdin);
//	freopen(".out","w",stdout);
	ll n;int k;
	cout.setf(ios::fixed);
	cout.precision(0);
	
	while (scanf("%lld%d",&n,&k)!=EOF)
	{
		if (n<=10000)
		{
			ld ans=0.0;
			For(i,n) ans+=log(i);
			ans/=log(k);
			ans=ceil(ans+eps);
			cout<<ans<<endl;
		}		
		else 
		{
			cout<<ll(0.5*log(2*pi*n,k)+n*log(n,k)-n*log(e,k))+1<<endl;
		}
		
	}
	
	
	return 0;
}
           

繼續閱讀