题意:给定n,选择一些小于等于n的数构成一个集合,使得集合内各个元素的十进制表示形式没有重叠的数字,问这样的集合有多少个。
分析:一共十个数字,我们可以枚举单个数字的所有组合情况(一千种),然后用数位DP求出符合每种组合的所有单个数字的个数,设f[x]表示数字集合为x的个数,我们可以利用用状压DP的方式求出f[x]的值。
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
#include <unordered_map>
#define INF 0x3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
using namespace std;
typedef long long ll;
int T,n,a[20],top[1050];
ll dp[20][1050],f[1050],sum[1050];
ll dfs(int pos,int sta,int limit,int lead,int num)
{
if(pos == -1)
{
if(sta == num) return 1ll;
return 0ll;
}
if(!limit && !lead && dp[pos][sta] >= 0) return dp[pos][sta];
int up = limit ? a[pos] : 9;
ll ans = 0;
for(int i = 0;i <= up;i++)
if((1<<i) & num || (!i && lead))
{
ans = (ans + dfs(pos-1,sta | ((lead && !i) ? 0 : (1<<i)),limit && i == up,lead && !i,num)) % MOD;
}
if(!limit && !lead) dp[pos][sta] = ans;
return ans;
}
ll solve(int x,int num)
{
int pos = 0;
while(x)
{
a[pos++] = x % 10;
x /= 10;
}
memset(dp,-1,sizeof(dp));
return dfs(pos-1,0,1,1,num);
}
int main()
{
int tot = (1<<(10))-1;
for(int i = 1;i <= tot;i++) top[i] = (1<<(int)log2(i));
scanf("%d",&T);
for(int t = 1;t <= T;t++)
{
scanf("%d",&n);
ll ans = 0ll;
memset(f,0,sizeof(f));
for(int i = 1;i <= tot;i++) sum[i] = solve(n,i);
for(int i = 1;i <= tot;i++)
{
int res = i - top[i];
for(int j = res;j;j = (j-1)&res)
f[i] = (f[i] + f[j]*sum[i-j]) % MOD;
f[i] = (f[i] + sum[i]) % MOD;
ans = (ans + f[i]) % MOD;
}
printf("Case %d: %lld\n",t,ans);
}
}