题目:http://acdream.info/problem?pid=1084
寒假又快要到了,不过对于lzx来说,头疼的事又来了,因为众多的后宫都指望着能和lzx约会呢,lzx得安排好计划才行。
假设lzx的后宫团有n个人,寒假共有m天,而每天只能跟一位后宫MM约会,并且由于后宫数量太过庞大了,而寒假的天数太少,所以lzx在寒假里不会与一个MM约会一次以上。现在lzx想要知道:寒假安排的方案数如果写成k进制,末位会有多少个0。
输入的第一行是一个整数,为数据的组数t(t<=1000)。
每组数据占一行,为3个正整数n、m和k(1<=m<=n<2^31,2<=k<2^31),意思如上文所述。
3 10 5 10 10 1 2 10 2 8
1 1 0
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
4
k进制素因子幂对应的个数:
5
1
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
4
k进制素因子幂对应的个数:
15
4
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; }