1. 程式人生 > 實用技巧 >【USACO】Cow Brainiacs

【USACO】Cow Brainiacs

題意描述

Cow Brainiacs

\(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\)

,那麼我們只需要求 \(a\)\(b\) 進製表示的第一位即可(因為 \(a\ mod \ b\neq 0\))。

實現方法是將 \(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;
}

完結撒❀。