[HAOI2010] 计数

你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数。比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等。

现在给定一个数,问在这个数之前有多少个数。(注意这个数不会有前导0).

Solution

可重复康托展开

  • 常用数位 dp 套路,枚举哪一位开始比原数小,前方唯一而后方算全排列

  • 回避高精度的全排列数计算,考虑到阶乘最多大约算到 50, 开个因子计数桶,然后在桶上操作就可以了

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1005;
char str[N];
int n;

int buc[N];

void push(int x) {
    for(int i=2;i<=x;i++) {
        while(x%i==0) buc[i]++, x/=i;
    }
}

void pop(int x) {
    for(int i=2;i<=x;i++) {
        while(x%i==0) buc[i]--, x/=i;
    }
}

int calc(vector <int> v) {
    int sum=0;
    for(int i=0;i<10;i++) sum+=v[i];
    for(int i=2;i<=sum;i++) push(i);
    for(int i=0;i<10;i++) {
        for(int j=2;j<=v[i];j++) pop(j);
    }
    int ans=1;
    for(int i=2;i<=50;i++) while(buc[i]) buc[i]--, ans*=i;
    return ans;
}

signed main() {
    cin>>str+1;
    n=strlen(str+1);
    vector <int> v;
    for(int i=0;i<10;i++) v.push_back(0);
    for(int i=1;i<=n;i++) v[str[i]-'0']++;
    int ans=0;
    for(int i=1;i<=n;i++) {
        for(int j=0;j<str[i]-'0';j++) {
            if(v[j]==0) continue;
            v[j]--;
            ans+=calc(v);
            v[j]++;
        }
        v[str[i]-'0']--;
    }
    cout<<ans<<endl;
}
原文地址:https://www.cnblogs.com/mollnn/p/12317129.html