ICPC Northeastern European Regional Contest 2019 Key Storage
题意:
给出一个数n,先除以2,所得到的商再除以3,商再除以4……直至商为0
过程中的余数记录下来
问有多少数与n经过这种操作得到的余数是相同的
一个数对应一个唯一的余数序列
一个余数序列也对应一个唯一的数
相当于把n表示成二进制,取走最后一位,右移一位后再表示成三进制,取走一位,右移一位后再表示成四进制……
所以一个数和余数序列是一一对应的
问题转换成已知一个序列,其中的数x只能放在位置>x的地方,问有多少种排列方式
从大到小用组合数加乘法原理解决
最后一个位置不能放0,所以再减去最后一个位置为0的方案数
注:有可能虽然序列有0,但是不存在将0放在最后一位的方案数,比如4,余数序列为{0,2},计算组合数时如果用n!/m!/(n-m)!可能会导致n<m
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int r[41],cnt[41],s[41]; long long C[41][41]; int main() { int T; scanf("%d",&T); C[0][0]=1; for(int i=1;i<=20;++i) { C[i][0]=1; for(int j=1;j<=i;++j) C[i][j]=C[i-1][j-1]+C[i-1][j]; } long long n,tmp,ans; int now; while(T--) { memset(s,0,sizeof(s)); memset(cnt,0,sizeof(cnt)); cin>>n; now=2; while(n) { cnt[n%now]++; n/=now; now++; } now--; for(int i=now-1;i;--i) s[i]=cnt[i]+s[i+1]; ans=1; for(int i=now-1;i;--i) if(cnt[i]) ans*=C[now-i-s[i+1]][cnt[i]]; if(cnt[0]) { tmp=1; for(int i=now-1;i;--i) if(cnt[i]) tmp*=C[now-i-1-s[i+1]][cnt[i]]; ans-=tmp; } cout<<ans-1<<'\n'; } }