荣耀时刻,记录一下,卡了
卡了三天,主要是网上没人写过,靠自己写了一道。
B进制阶乘最右非零位 HRBUST - 1860
题意:
给你一个n,和b
问你:
n
!
n!
n!在b进制下的最右边的非零数字。
思路:
题目保证m为质数,所以可以知道,当 ∏ 1 n \prod_{1}^{n} ∏1n中出现m的倍数时,要先除掉,计算到最后,那么就是答案。(记住要%m)
下面是暴力代码
int brute_force(ll n, ll mod){
ll res = 1;
cout<<"res-------"<<endl;
for(int i = 1; i <= n; i ++ ){
res*=i;
while(res%mod==0)res/=mod;
res%=mod;
cout<<res<<' ';
if(i%mod==0)cout<<endl;
}
cout<<endl;
return res;
}
时间复杂度的分析,
题目只有1s,而case数有100,n最大到 1 0 9 10^9 109,所以直接暴力循环。铁超时,所以要想下怎么优化。
先预处理出,【1,m】中所有阶乘的最后一位非零数,方便后面计算用。
具体观察,上面的图
步骤1:先考虑下面一排
- 对于(1,mod)之间的数(不包含区间端点),类似地(mod,2mod),(2mod,3mod),。。。。
可以发现这中间的数,模运算后,还是不变,
- 由于中间的数都一样,所以直接快速幂就行。(根据前面预处理)
步骤2:再考虑上面一排。
之后剩下没计算的就剩下上面一排的元素1,2,3,…,1
而对于这里,聪明的读者可能已经发现了,这无非是重复步骤1(把上面一排的元素,再分块),之后步骤2.(直到没有元素)。
AC
/*
皮卡丘冲鸭!
へ /|
/\7 ∠_/
/ │ / /
│ Z _,< / /`ヽ
│ ヽ / 〉
Y ` / /
イ● 、 ● ⊂⊃〈 /
() へ | \〈
>ー 、_ ィ │ //
/ へ / ノ<| \\
ヽ_ノ (_/ │//
7 |/
>―r ̄ ̄`ー―_
*/
#include <iostream>
#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) for(int i=(x); i<(y); i++)
#define rep(i,y,x) for(int i=(y); i>=(x); i--)
#define mst(x,a) memset(x,a,sizeof(x))
#define pb push_back
#define sz(a) (int)a.size()
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int>pa;
typedef pair<ll,ll>pai;
const int N = 1e6+10;
const int M = 1e5;
//ll st[N];
ll qpow(ll a, ll k, ll mod){
ll res = 1;
while(k){
if(k&1){
res=res*a;//%mod;
// while(res%mod==0)res/=mod;
res%=mod;
}
a = a*a%mod;
k>>=1;
}
return res;
// do_nextpermuation();
}
int v[N];
//a[N];
int brute_force(ll n, ll mod){
ll res = 1;
cout<<"res-------"<<endl;
for(int i = 1; i <= n; i ++ ){
res*=i;
while(res%mod==0)res/=mod;
res%=mod;
cout<<res<<' ';
if(i%mod==0)cout<<endl;
}
cout<<endl;
return res;
}
int main()
{
//cout<<qpow(2,10,1e14);
// ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int tt,kase=0;//cin>>tt;
scanf("%d", &tt);
while(tt--){
ll n, mod;
// cout<<"-----"<<endl;
scanf("%lld %lld", &n, &mod);//cin>>n>>mod;
//vector<int> v(mod+1);
v[0]=v[1]=1;
ll res = 1;
For(i,1,mod){
res*=i;
while(res%mod==0)res/=mod;
res%=mod;
v[i]=res;
}
//cout<<"ok"<<endl;
res = 1;
/*
For(i,1,n){
res*=i;
while(res%mod==0)res/=mod;
res%=mod;
cout<<res<<' ';
}
*/
//cout<<endl;
/*
cout<<"v[n]-----------"<<endl;
For(i,1,mod)cout<<v[i]<<' ';
cout<<endl;
*/
if(n<mod)res = v[n];//cout<<v[n]<<endl;
else {
ll num = n, num2 = n/mod;
res = 1;
while(num){
int reminder = num%mod;
int reminder2 = (num/mod)%mod;
/// ll a1=v[reminder2]*qpow(v[mod-1],reminder2,mod);
///res = res*v[reminder]*a1%mod;
num2 = num/mod;
res = res*qpow(v[mod-1],num2,mod)%mod;
res = res*v[reminder]%mod;
num/=mod;
}
/// int reminder = n%mod;
// while(n){
/// ll num = n/mod;
// vector<int> a(mod+1);
/// a[0] = a[1] = 1;
/// res = 1;
/// 是这里TLE了
/*
For(i,1,mod){
res = res*v[mod-1]*i;
while(res%mod==0)res/=mod;
res%=mod;
a[i] = res;
}
*/
/*
res = 1;
int reminder2 = num%mod;
/// printf("reminder=%d, reminder2 = %d, num = %lld\n", reminder, reminder2, num);
ll num2 = num/mod;
ll a1=v[reminder2]*qpow(v[mod-1],reminder2,mod);
//while(a1%mod==0)a1/=mod;
a1%=mod;
ll a2 = v[mod]*qpow(v[mod-1],mod,mod);
//while(a2%mod==0)a2/=mod;
res = v[reminder]*a1%mod;
if(num>=mod){
//int reminder2 = num%mod;
// for(int i = 1; i <= num/mod; i++)res = res*a[mod]%mod;
res = res*qpow(a2,num/mod,mod)%mod;//*a[reminder2]%mod;
//res = res*v[reminder1]%mod;
}
*/
// res = qpow(v[mod-1],n,mod)*v[reminder]%mod;
//}
}
//Case 1: 2
printf("Case %d: %lld\n", ++kase, res);//cout<<"Case "1: "res<<endl;
/// printf("Case %d: brute_force=%lld\n", kase, brute_force(n,mod));//cout<<"Case "1: "res<<endl;
}
return 0;
}
/*
299 11
case: 2
bf: 4
*/