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';
    }
}

 

posted @ 2020-09-06 16:36  TRTTG  阅读(223)  评论(0编辑  收藏  举报