【dfs 组合数】code forces 991E Bus Number
题目内容
给一个看到的数n(1<=n<=1e18),无前导0,求实际的数x有多少种可能。
n与x的关系是,
1.n中出现的数,x中也出现。
2.x中任何一个数出现的次数不超过其在n中出现的次数。
样例
input
97
output
2
input
2028
output
13
Note
In the first sample, only variants 97 and 79 are possible.
In the second sample, the variants (in the increasing order) are the following: 208 , 280 , 802 , 820 , 2028 , 2082 , 2208 , 2280 , 2802 , 2820 , 8022 , 8202 , 8220 .
解题思路
1e18范围内最多就只有19个数,先把0,1,2……的数量都统计出来,dfs找到不同数量的组合,然后直接通过组合数公式累计答案,比如,对于一个总数量为m的组合,就是先在m-1个位置里挑出0的位置,再挑1的位置……
AC代码
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 20;
const LL mod = 1e18+7;
const int INF=0x3f3f3f3f;
LL n,m;
LL ans=0;
LL cnt[maxn];
struct node
{
LL num,x;
}a[maxn];
int cmp(node a,node b)
{
return a.num>b.num;
}
LL C[maxn][maxn];
void get_C(LL x)
{
C[0][0] = 1;
for(int i=1;i<=x;i++)
{
C[i][0] = 1;
for(int j=1;j<=i;j++)
{
//printf("(%d,%d)\n",i,j);
C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod;
}
}
}
void get_ans()
{
LL sum=0,y=1;
for(LL i=0;i<=m;i++)
{
sum+=cnt[i];
}
if(cnt[0]==sum) return;
if(cnt[0]>0) y=y*C[sum-1][cnt[0]]%mod;
sum-=cnt[0];
for(LL i=1;i<=m;i++)
{
y=y*C[sum][cnt[i]]%mod;
sum-=cnt[i];
}
ans+=y;
}
void dfs(LL x)
{
//printf("(%lld,%lld)\n",x,m);
if(x==m+1)
{
get_ans();
return;
}
for(int i=1;i<=a[x].num;i++)
{
cnt[x]=i;
dfs(x+1);
}
}
int main()
{
LL i,j,k,x,y=0;
scanf("%lld",&n);
get_C((LL)18);
//printf("E");
while(n>0)
{
cnt[n%10]++;
n/=10;
}
for(i=0;i<=9;i++)
{
a[i].x=i;
a[i].num=cnt[i];
y+=cnt[i];
}//printf("A");
if(y==cnt[0])
{
printf("1\n");
return 0;
}
sort(a+1,a+10,cmp);
m=0;//printf("B");
for(i=1;i<=9;i++)
{
if(a[i].num>0) m++;
}//printf("C");
if(cnt[0]>0)
{
ans=0;
dfs(0);//printf("D");
}
else
{
cnt[0]=0;
dfs(1);
}
printf("%lld\n",ans);
return 0;
}