• Hihocder 1639 : 图书馆 (组合数+唯一分解 求最后一位)(妙)


     给定n,(n<=10^3),然后输入n的数a[i],(a[i]<=1e10),求ans=(a1+a2+a3...an)! / (a1!*a2!*a3!...an!) 的结果的最一位数。

    适用问题,n种物品,求全排种类,结果%10。

     

    猜想1,斯特林公式,斯特林公式虽然误差越来越小,但是最后一位的误差是难以消除的,虽然求位数还稳,但是求最后一位几乎不会对。

    猜想2,a[]达到一定程度,答案是0,此种情况必须保证ans的2因子和5因子数都>0,小范围唯一分解,但依然有30%数据过不去。

    猜想3:唯一分解,但是素数太多,而且又得具体到每一个的逆元,难以实现。

     

    猜想4:将ans转化为5的倍数*非5的倍数,以及2的倍数以及非2的倍数,然后剩余定理得计解。

                即,将ans%10,改为%素数p,p=2时:ans2=ans%2;      p=5时:ans5=ans%5,然后中国剩余定理得到ans%10;

                以5为例,算出分解后5因子的个数x:令ans5=((5^x) *y)/z%5,如果x>0,则ans5%5=0;否则得到分子的y和z。 得到ans5=(y/z)% 5。

    得到x的具体实现:

              以5为例,N!= (5^x)*y ,则x=N/5+N/5/5+N/5/5/5...。对于不是5的倍数的部分,以5为循环节计算。 

              坑点在于:5的倍数里面的数一定要算干净,所以要一层一层继续算系数,如:100=5*5*2,这个2是有用的,我就是这里没想到然后挂了。

       1,2,3,4,5,6,7,8,9,10,11...=1,2,3,4,1*5,1,2,3,4,2*5,1...  (循环节为5) = [ (24%5)^ (n/5) ]%5 * (5^x) * 1*2*3 ...(后面的123是系数)

     

    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    #define ll long long
    ll a[10],ans2,ans5, x[1100],sum;
    int qpow(int n,ll m,int Mod)
    {
        int res=1;n%=Mod;
        while(m){
            if(m&1) res=n*res%Mod;
            n=n*n%Mod;
            m>>=1;
        } return res;
    }
    void get(int opt,ll x,int sig)
    {
        ll tmp=x;
        if(sig==1||sig==-1){
            if(tmp){
                a[opt]+=sig*(tmp/opt);
                tmp/=opt;
            }
            if(tmp){
                get(opt,tmp,1*sig);    //5的倍数的系数不要搞忘 
                get(opt,tmp,2*sig);
            }
        }
        else{
            tmp=1;
            for(int i=1;i<opt;i++){
                tmp=tmp*i%opt;
            }
            tmp=qpow(tmp,x/opt,opt);
            for(ll i=(x/opt)*opt+1;i<=x;i++)  tmp=tmp*(i%opt)%opt;
            if(opt==2&&sig==2)  a[6]=a[6]*tmp%opt;
            if(opt==5&&sig==2)  a[7]=a[7]*tmp%opt;
            if(opt==2&&sig==-2) a[8]=a[8]*tmp%opt;
            if(opt==5&&sig==-2) a[9]=a[9]*tmp%opt;
        }
    }
    int main()
    {
        int T,n;
        scanf("%d",&T);
        while(T--){
            sum=0; ans2=ans5=0;
            a[2]=a[5]=0; a[6]=a[7]=a[8]=a[9]=1;
            scanf("%d",&n);
            for(int i=1;i<=n;i++){
                scanf("%lld",&x[i]);
                sum+=x[i];
            }
            get(2,sum,1);    //正数表示在分子 
            get(2,sum,2);
            get(5,sum,1);   //1表示2或5的幂,可以加。  
            get(5,sum,2);   //1表示非2或5的幂。  
            for(int i=1;i<=n;i++){
                get(2,x[i],-1); //负数表示在分母 
                get(2,x[i],-2);
                get(5,x[i],-1);
                get(5,x[i],-2);
            }
            if(a[2]>0&&a[5]>0){ //下面的可以不算,但是算也花不了多少时间。 
                printf("0
    ");
                continue;
            }
            
            ans2=qpow(2,a[2],2);
            ans2=ans2*a[6]%2;
            ans2=ans2*qpow(a[8],1,2)%2;
        
            ans5=qpow(5,a[5],5);
            ans5=ans5*a[7]%5;
            ans5=ans5*qpow(a[9],3,5)%5;
            
            printf("%lld
    ",(5*ans2+16*ans5)%10); //5和16都是逆元算的
        } return 0;
    }
  • 相关阅读:
    前端笔试题----JavaScript部分
    前端笔试题----html,css部分
    JS基础--执行环境及作用域
    关于css3 flex布局
    Ceph万兆内网与系统万兆迁移
    从0开始的InfiniBand硬件踩坑过程
    Redis实战与分析
    ceph osd 自动挂载的N种情况
    集群IPtables转发与防火墙
    ceph 常见问题百科全书---luminous安装部署篇
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8446881.html
Copyright © 2020-2023  润新知