BZOJ2425: [HAOI2010]计数
Description
你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数。
比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等。
现在给定一个数,问在这个数之前有多少个数。(注意这个数不会有前导0).
Input
只有1行,为1个整数n.
Output
只有整数,表示N之前出现的数的个数。
Sample Input
1020
Sample Output
7
HINT
n的长度不超过50,答案不超过263-1.
题解Here!
这题思路和数位$DP$差不多。
一位一位向前推进。
首先我们知道,对于一个数,把其中的$0$删掉,相当于把$0$放到了前面。
所以这个问题就是让我们求一下给我们的数字的全排列中比当前小的有几个。
设$num[i]$代表数字$i(i\in[0,9])$有几个。
那么全排列就是这个式子:
$$\frac{(\sum_{i=0}^9num[i])!}{\prod_{i=0}^9num[i]!}$$
当然如果$num[i]==0$那么不参与运算。。。
但是这样的话$long\ long$表示存不下。
我已经说了我不会再写高精度的!
所以继续想优化。
假设现在有$m$个位置。
我们先把$0$放法放好:
$$C_m^{num[0]}$$
之后就只有$m-num[0]$个位置。
然后再放$1$:
$$C_{m-num[0]}^{num[1]}$$
以此类推。。。
所以答案长这个样:
$$Ans=C_m^{num[0]}\times C_{m-num[0]}^{num[1]}\times...\times C_{m-num[0]-num[1]-...-num[8]}^{num[9]}$$
然后这个组合数就用杨辉三角什么的乱搞就好了。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 1010 using namespace std; int n,val[MAXN],num[MAXN]; long long triangle[MAXN][MAXN]; inline void read(){ char c=0; n=0; while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9'){val[++n]=c-'0';num[val[n]]++;c=getchar();} } long long C(long long n,long long m){ if(triangle[n][m])return triangle[n][m]; if(m==1)return n; if(m==0||m==n)return 1; if(m>n)return 0; triangle[n][m]=C(n-1,m)+C(n-1,m-1); return triangle[n][m]; } long long solve(int x){ long long s=1; for(int i=0;i<=9;i++) if(num[i]){ s*=C(x,num[i]); x-=num[i]; } return s; } void work(){ long long ans=0; for(int i=1;i<=n;i++){ for(int j=0;j<val[i];j++) if(num[j]){ num[j]--; ans+=solve(n-i); num[j]++; } num[val[i]]--; } printf("%lld\n",ans); } int main(){ read(); work(); return 0; }