B进制阶乘最右非零位 HRBUST - 1860(分块???我也不知道是不是分块,反正是一直分解下去)

荣耀时刻,记录一下,卡了
在这里插入图片描述
卡了三天,主要是网上没人写过,靠自己写了一道。

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. 对于(1,mod)之间的数(不包含区间端点),类似地(mod,2mod),(2mod,3mod),。。。。可以发现这中间的数,模运算后,还是不变,
  2. 由于中间的数都一样,所以直接快速幂就行。(根据前面预处理
步骤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
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值