1. 程式人生 > >NBUTOJ 1643 - 階乘除法 - [數學題]

NBUTOJ 1643 - 階乘除法 - [數學題]

inpu 兩個 lan n+1 oid 答案 ont titles pow

題目鏈接:https://ac.2333.moe/Problem/view.xhtml?id=1643

  • 問題描述
  • 輸入兩個正整數 n, m,輸出 n!/m!,其中階乘定義為 n!= 1*2*3*...*n (n>=1)。 比如,若 n=6, m=3,則 n!/m!=6!/3!=720/6=120。

    是不是很簡單?現在讓我們把問題反過來:輸入 k=n!/m!,找到這樣的整數二元組(n,m) (n>m>=1)。

    如果答案不唯一,n 應該盡量小。比如,若 k=120,輸出應該是 n=5, m=1,而不是 n=6, m=3,因為 5!/1!=6!/3!=120,而 5<6。

  • 輸入
  • 輸入包含不超過 100 組數據。每組數據包含一個整數 k (1<=k<=10^9)。
  • 輸出
  • 對於每組數據,輸出兩個正整數 n 和 m。無解輸出"Impossible",多解時應讓 n 盡量小。
  • 樣例輸入
  • 120
    1
    210
  • 樣例輸出
  • Case 1: 5 1
    Case 2: Impossible
    Case 3: 7 4

題解:

首先,impossible的情況只有1;並且所有k為奇數的情況,只能up = K, down = K - 1;

一開始,我想的是對於K,直接暴力枚舉up = 1~K,篩掉k%up != 0的,然後剩下的只能while循環去除up - 1、up - 2、up - 3……這樣,然後發現,TLE在O(K)枚舉上;

然後改換思路,考慮到對於任意一個K,若 n × (n+1) × (n+2) × … × (n+len) = K,則顯然len不可能大於p - 1,其中p滿足p! > K 且 (p-1)! < K;

then,枚舉len = p-1 ~ 1:對於每個len,down必須滿足pow(down,len) < K,然後從1開始枚舉down,算出 (down+1) × (down+2) × … × (down+len),判斷一下即可。

最後考慮到之前O(K)枚舉超時,所以特判當len=1時直接輸出up = K, down = K - 1.

AC代碼:

#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
ll fact[15];
void init()
{
    fact[
0]=1; for(int i=1;i<=13;i++) { fact[i]=i*fact[i-1]; //printf("fact[%d]=%I64d\n",i,fact[i]); } } ll k; int main() { init(); int kase=0; while(scanf("%I64d",&k)!=EOF) { if(k==1) { printf("Case %d: Impossible\n",++kase); continue; } if(k%2==1) { printf("Case %d: %I64d %I64d\n",++kase,k,k-1); continue; } int maxlen; bool ok=0; for(int i=1;i<=13;i++) { if(fact[i]==k) { printf("Case %d: %d %d\n",++kase,i,1); ok=1; break; } if(fact[i]>k && fact[i-1]<k) { maxlen=i-1; break; } } if(ok) continue; ok=0; for(int len=maxlen;len>=1;len--) { if(len==1) { printf("Case %d: %I64d %I64d\n",++kase,k,k-1); break; } for(ll down=1;pow((double)down,(double)len)<(double)k;down++) { ll prod=1; for(int i=1;i<=len;i++) prod*=down+i; if(prod==k) { printf("Case %d: %I64d %I64d\n",++kase,down+len,down); ok=1; break; } if(prod>k) break; } if(ok) break; } } }

NBUTOJ 1643 - 階乘除法 - [數學題]