ACdream 1084 寒假安排(数论(k进制末位的0的数目+n!中v因子个数))

题目:http://acdream.info/problem?pid=1084

寒假安排

Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others)

Problem Description

寒假又快要到了,不过对于lzx来说,头疼的事又来了,因为众多的后宫都指望着能和lzx约会呢,lzx得安排好计划才行。

假设lzx的后宫团有n个人,寒假共有m天,而每天只能跟一位后宫MM约会,并且由于后宫数量太过庞大了,而寒假的天数太少,所以lzx在寒假里不会与一个MM约会一次以上。现在lzx想要知道:寒假安排的方案数如果写成k进制,末位会有多少个0。

Input

输入的第一行是一个整数,为数据的组数t(t<=1000)。

每组数据占一行,为3个正整数n、m和k(1<=m<=n<2^31,2<=k<2^31),意思如上文所述。

Output

对于每组数据,输出一个数,为寒假安排的方案数写成k进制末位的0的数目。

Sample Input

3
10 5 10
10 1 2
10 2 8

Sample Output

1
1
0
分析:
求n!中v因子个数的做法

ll go(ll n, ll v){  
    ll ans = 0;
    ll tmp = v;
    while(n>=tmp){  //如果 tmp>n,另一个因子会小于1.
        ans += n/tmp;
        tmp*=v;
    }
    return ans;
}
或:
1*2*3*4*…*n = p^(n/p)*(1*2*,,,,*(n/p))*[原因子中不能被p整除的书的乘积]  那么 f[n][p] = n/p + f[n/p][p],就可以通过递归的方法求出了。
LL getnum(int n,int p){
  if(n<p) return 0;
  return n/p+getnum(n/p,p);
}

A(n,m)=n!/(n-m)!=n(n-1)(n-2)*……*(n-m+1)  取k的因子最小的数目即是结果。
用素因子分解:
int prime[maxn],top1=0;
bool notprime[maxn];
void getprime(){
    for(int i=2;i<=maxn;i++){
        if(!notprime[i])prime[top1++]=i;
        for(int j=0;j<top1&&i*prime[j]<=maxn;j++){
            notprime[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
maxn的拿捏不容易啊,开大啦可能超时,开小了会数组越界。直接分解吧。--》为什么开大了会超时?看来在一定情况下线性筛法+分解不一定就比直接分解要好。

关于k的因子最小的数目 的解释:A(n,m)转换成k进制有多少个零相当于它能连续被几个k整除,因此将k进行素因子分解,例如20=2^2*5,阶乘有几套k的素因子就有几个零。A(n,m)=n!/(n-m)!=n(n-1)(n-2)*……*(n-m+1) “A(n,m)除以k的素因子,等于n!除以k的素因子减去(n-m)!除k以的素因子”--》s[i]=getsum(n,fac[i])-getsum(n-m,fac[i]); 最后查看有几套:loop: minm=min(s[i]/fac[i],minm) 整个循环的结果就是"几套k的素因子"。
现在先见一个例子:

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
LL getzero(LL n,LL v){
    if(n<v)return 0;
    return n/v+getzero(n/v,v);
}
const LL maxn=1e6+10;
LL sta[maxn],pnum[maxn],top;
void fenjie(LL m){
    top=0;
    memset(pnum,0,sizeof(pnum));
    memset(sta,0,sizeof(sta));
    for(LL i=2;i*i<=m;i++){
        if(m%i==0){
            sta[top]=i;
            while(m%i==0){
                m/=i;
                pnum[top]++;
            }
            top++;
        }
    }
    if(m>1){
        sta[top]=m;
        pnum[top++]++;
    }
}
LL pnum1[maxn],pnum2[maxn];
LL sys[maxn],stp;
void turn(LL m,LL v){
    stp=0;
    while(m>0){
        sys[stp++]=m%v;
        m/=v;
    }
}
LL factorial(int m){
    if(m==2) return 2;
    return m*factorial(m-1);
}
LL facA(int n,int m){
    LL ans=1,aq=n-m;
    while(n>aq){
        ans*=n;
        n--;
    }
    return ans;
}
int main()
{
    freopen("cout.txt","w",stdout);
    LL n,m,k;
    while(cin>>n>>m>>k){
        fenjie(k);
        memset(pnum1,0,sizeof(pnum1));
        memset(pnum2,0,sizeof(pnum2));
        cout<<"n!= "<<factorial(n)<<endl;
        turn(factorial(n),k);
        printf("n! %d进制:\n",k);
        for(LL i=stp-1;i>=0;i--)cout<<sys[i]; cout<<endl;
        cout<<"(n-m)!= "<<factorial(n-m)<<endl;
        turn(factorial(n-m),k);
        printf("(n-m)! %d进制:\n",k);
        for(LL i=stp-1;i>=0;i--)cout<<sys[i]; cout<<endl;
        for(LL i=0;i<top;i++){
            pnum1[i]=getzero(n,sta[i]);
            pnum2[i]=getzero(n-m,sta[i]);
        }//最小的getzero因子个数“就是”阶乘数末尾的0的个数
        cout<<"素因子对应的个数:\n";
        for(LL i=0;i<top;i++)cout<<pnum1[i]<<" ";  cout<<endl;
        for(LL i=0;i<top;i++)cout<<pnum2[i]<<" ";  cout<<endl;
        cout<<"k进制素因子幂对应的个数:\n";
        for(LL i=0;i<top;i++)cout<<pnum1[i]/pnum[i]<<" ";  cout<<endl;
        for(LL i=0;i<top;i++)cout<<pnum2[i]/pnum[i]<<" ";  cout<<endl;
        //A(n,m)=n!/(n-m)!,所以A(n,m)末尾0的个数“就是”pnum1[i]-pnum2[i]的最小值。
        //不是pnum1[i]的最小值-pnum2[i]的最小值的啊,有可能在这之间产生新的0.
        //问题是寻找k进制对应的末尾0,所以最后比较要除以k的各个素因子个数。
        //不然就相当于找到了k'进制的A(n,m)末尾0的个数。其中k'是所有k的素因子的一次幂的乘积。
        cout<<"A(n,m)= "<<facA(n,m)<<endl;
        LL k_k=1;
        for(int i=0;i<top;i++){
            k_k*=sta[i];
        }
        turn(facA(n,m),k_k);
        cout<<"对应k_k进制:\n";
        for(LL i=stp-1;i>=0;i--)cout<<sys[i]; cout<<endl;

        LL minm=9999999999;
        for(LL i=0;i<top;i++){  //对应k'(k_k)
            LL res=pnum1[i]-pnum2[i];
            minm=min(minm,res);
        }
        cout<<"minm="<<minm<<endl;

        turn(facA(n,m),k);
        cout<<"对应k进制:\n";
        for(LL i=stp-1;i>=0;i--)cout<<sys[i]; cout<<endl;
        minm=9999999999;
        for(LL i=0;i<top;i++){   //对应k
            LL res=(pnum1[i]-pnum2[i])/pnum[i];
            minm=min(minm,res);
        }
        cout<<"minm="<<minm<<endl;
    }
    return 0;
}

输入两个例子:

16 9 8

16 9 2

结果对照:

n!= 20922789888000
n! 8进制:
460356735300000
(n-m)!= 5040
(n-m)! 8进制:
11660
素因子对应的个数:
15 

k进制素因子幂对应的个数:


A(n,m)= 4151347200
对应k_k进制:
11110111011100001000100000000000
minm=11
对应k进制:
36734104000
minm=3

n!= 20922789888000
n! 2进制:
100110000011101110111011101011000000000000000
(n-m)!= 5040
(n-m)! 2进制:
1001110110000
素因子对应的个数:
15 

k进制素因子幂对应的个数:
15 

A(n,m)= 4151347200
对应k_k进制:
11110111011100001000100000000000
minm=11
对应k进制:
11110111011100001000100000000000
minm=11

应该懂了~~
代码:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int  getnum(int  n,int  p){  //n!的p因子的个数
  if(n<p) return 0;
  return n/p+getnum(n/p,p);
}
const int maxn=1e5;
int sta[maxn],pnum[maxn],top2;
void fenjie(int m){
    top2=0;
    memset(sta,0,sizeof(sta));
    memset(pnum,0,sizeof(pnum));
    for(int i=2;i*i<=m;i++){
        if(m%i==0){
            sta[top2]=i;
            while(m%i==0){
                m/=i;
                pnum[top2]++;
            }
            top2++;
        }
    }
    if(m>1){
        sta[top2]=m;
        pnum[top2++]++;
    }
}
int pn[maxn];
int main()
{
    //freopen("cin.txt","r",stdin);
    int t,n,m,k;
    cin>>t;
    while(t--){
        scanf("%d%d%d",&n,&m,&k);
        fenjie(k);
        memset(pn,0,sizeof(pn));
        for(int i=0;i<top2;i++){
            pn[i]=getnum(n,sta[i]);
            pn[i]-=getnum(n-m,sta[i]);
        }
        int ans=pn[0]/pnum[0];
        for(int i=1;i<top2;i++){
            int qr=pn[i]/pnum[i];
            ans=ans<qr?ans:qr;
        }
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(数学,ACdream)