【CF1267K】Key Storage——数论 排列组合 c++

根据题意可以得知,每一个n值对应唯一的序列,即同样的n对应同样的序列,那么就可以转换成不同的序列对应不同的​n

由此我们可以求出n对应的序列。而这个序列的每一种排列都对应一个数。那么答案就是这个序列排列组合的数量。

当然,有两个条件需要满足:

1.由于是余数,所以第​i位上的数小于i+1

2.最后一位不能为0,因为那样在倒数第二位时​n就已经为0,就会提前结束。

对于第一个条件,我们会发现对于任意​x>y,那么​x能选的位置​y一定能选,但y能选的位置x不一定能选。因此我们从大往小挨个计算可行的情况即可。

对于第二个条件,我们只需按照最后一位可以为0的情况计算结果,再计算最后一位一定是0​的结果(把​0放到最后一位上),两数相减便是答案。

数组开到20就可以了。

是否预处理C无所谓,反正范围挺水的

#include<bits/stdc++.h>
#define int long long
using namespace std;
int b[100];
int bl;
int c[100][100];//j选i个 
void init()//预处理C 
{
    for(int j=0;j<=20;j++) c[0][j]=1;
    for(int i=1;i<=20;i++)
    {
        for(int j=i;j<=20;j++)
        {
            c[i][j]=c[i-1][j-1]+c[i][j-1];
        }
    }
}
void fen(int x)//x对应的序列 
{
    int k=2;
    while(x)
    {
        b[x%k]++;
        x/=k;
        k++;
        
        bl++;//bl是序列的总元素数量 
    }
    return;
}
int pin()
{
    int ans=1;
    int s=0;
    for(int i=20;i>=0;i--)
    {
        if(!b[i]) continue;
        ans*=c[b[i]][min(bl-i+1,bl)-s];//排列组合 
        s+=b[i];
    }
    return ans;
}
signed main()
{
    init();
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        memset(b,0,sizeof(b));
        bl=0;
        fen(n);
        int x=pin();
        int y=0;
        if(b[0])
        {
            b[0]--;
            bl--;
            y=pin();//最后一位一定是零的情况 
        }
        cout<<x-y-1<<endl;
    }
    return 0;
}

 

posted @ 2021-02-22 18:17  陈冠宇  阅读(66)  评论(0编辑  收藏  举报