题目大意:给你一个最多50位的数,求在所有通过由他去掉0和重新排列数字顺序能产生的数中,比他小的有多少个
题解:构成的数是原数的排列(包含前导0)
记录0−9每个数字出现的个数num[]
类似数位dp那样按位考虑,当前考虑到第i位,记原数第i位为x,有两种决策
1.第i位取0−−x−1,后面n−i个数随便取,是一个有重复元素的全排列,根据公式算即可
2.第i位取x,num[x]−−,继续考虑下一位
我的收获:计数,按位,质因数分解处理阶乘
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
const int M=55;
const int p[]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
int n,num[10],mi[25];
ll ans;
char s[M];
void change(int x,int k){
for(int i=1;x!=1;i++){
while(!(x%p[i])) x/=p[i],mi[i]+=k;
}
}
void fac(int x,int f){
for(int i=1;i<=x;i++)
change(i,f);
}
ll calc(int x){
memset(mi,0,sizeof(mi));
fac(x,1);
for(int i=0;i<=9;i++) fac(num[i],-1);
ll ret=1;
for(int i=1;i<=15;i++) ret*=pow(p[i],mi[i]);
return ret;
}
void work()
{
for(int i=1;i<=n;i++){
for(int j=0;j<s[i]-'0';j++)
if(num[j]) num[j]--,ans+=calc(n-i),num[j]++;//这里要加回来,因为并不是固定的决策
num[s[i]-'0']--;
}
printf("%lld\n", ans);
}
void init()
{
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;i++) ++num[s[i]-'0'];
}
int main()
{
init();
work();
return 0;
}