【USACO】Cow Brainiacs
阿新 • • 發佈:2020-08-09
題意描述
求 \(n!\) 在 \(b\) 進製表示下的第一位非 \(0\) 位的數字。
演算法分析
閒話
忙人自動略過
之前做過一道 \(10\) 進製表示下的題目,感覺差不多。
一開始沒什麼思路,就隨手打了個暴力進去,結果竟然 AC 了。(只能說資料水)
n=read(),b=read();
for(int i=2;i<=n;i++){
f*=i;
while(f%b==0)
f/=b;
f%=b;
}
開開心心交給 GY,結果 \(2\) 分鐘之後被 Hack 了。
對於 15 10
,顯然答案為 \(8\),但是程式給出的結果為 \(3\)
被 Hack 的結果是我只記錄了末尾 \(1\) 位的數值,由於 \(14!\) 的末尾為 \(2\),所以 \((2\times 15)\ mod\ 10=3\)。
所以對於十進位制來說,記錄末尾 \(7\) 位就不會有問題了,但是對於 \(b\) 進位制來說卻遠遠不夠,只能另尋他路。
下面是正解。
正解
終於要 BB 正解了
讓我們假設 \(n!\) 的 \(b\) 進製表示的末尾並沒有 \(0\)(即 \(n!\ mod\ b\neq 0\)),那麼答案只要保留一位不斷 \(mod\ b\) 即可。
雖然顯然幾乎不可能有這種情況發生,但是我們可以構造這種情況。
倘若我們把 \(n!\) 表示為 \(b^k\times a\)
實現方法是將 \(b\) 質因數分解,然後將 \(n!\) 都質因數分解,將 \(b\) 的質因子減去殆盡即可。
具體看程式碼吧。
程式碼實現
#include<cstdio> #include<cstring> #include<iostream> #include<cmath> #include<algorithm> #define N 20 using namespace std; int n,b,tot[N],num[N]; int pri[N],cnt=0; int prime[N]={0,2,3,5,7,11,13,17,19,23,29}; //B 的質因子只有這 10 中可能。 int read(){ int x=0,f=1;char c=getchar(); while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' && c<='9') x=x*10+c-48,c=getchar(); return x*f; } int main(){ n=read(),b=read(); memset(tot,0,sizeof(tot)); memset(num,0,sizeof(num)); int bb=b; for(int i=1;i<=10;i++){ if(bb%prime[i]!=0) continue; pri[++cnt]=prime[i];//記錄 B 的質因子的種類。 while(bb%prime[i]==0) bb/=prime[i],++num[cnt]; //記錄 B 的質因子個數。 } int ans=1; for(int i=1;i<=n;i++){ int now=i; for(int j=1;j<=cnt;j++) while(now%pri[j]==0) now/=pri[j],++tot[j];//記錄其中 B 的質因子的個數。 ans=(ans*now)%b;//剩餘部分直接計算即可。 } int z=0x3f3f3f3f; for(int i=1;i<=cnt;i++) z=min(z,tot[i]/num[i]); for(int i=1;i<=cnt;i++) tot[i]-=num[i]*z;//將 B 的質因子減去殆盡。 for(int i=1;i<=cnt;i++) for(int j=1;j<=tot[i];j++) ans=(ans*pri[i])%b;//剩下的直接乘即可。 printf("%d\n",ans); return 0; }
完結撒❀。