题意:定义对于一个数字,那一个位数的数字出现最多,就定义为那个数字。比如133344就是状态3,111122就是状态1,对于出现次数不唯一的,定义为状态10.现在给一个区间问
(
l
,
r
)
(l,r)
(l,r)中有多少状态为
d
d
d的数,
d
d
d不为10.
做法:看一眼数位dp,然后状态怎么记录,考虑记录0-9每一个数字出现的次数,可以使用字符串,
v
e
c
t
o
r
vector
vector等使用
m
a
p
map
map标记,可以赌一下状态不会很多,甚至使用哈希,但不过这道题显然不会让你这么做,那么就需要优化。
首先对于记录状态的
s
t
a
sta
sta来说,那么即使数字出现的位置不用那么也是相同的。这个优化,让我学到了。
举个例子来说
1
−
12345678
1-12345678
1−12345678来说,如果遍历到
122
x
x
x
x
x
122xxxxx
122xxxxx其中
x
x
x范围全是
0
−
9
0-9
0−9。如果我们2成为众数,有多少中方案。这个时候就需要我们使用更快的方法求到答案。
初始一共有
x
x
x个位置。那么我们可以枚举我们的众数填放的次数
i
i
i,我们考虑一个
d
p
[
p
]
dp[p]
dp[p],表示填完
p
p
p个位置的方案数,那么初始化就是
d
p
[
i
]
=
C
x
i
dp[i]=C_{x}^i
dp[i]=Cxi,然后枚举其他数填放的次数
j
j
j, 那么就有如下递推
d
p
[
i
+
j
+
k
]
=
C
x
−
i
−
k
j
d
p
[
i
+
k
]
dp[i+j +k]=C_{x-i - k}^jdp[i + k]
dp[i+j+k]=Cx−i−kjdp[i+k]其中
k
k
k表示的是除了当前枚举的数和众数占的位置。代码的
d
p
dp
dp是倒着写的。
然后对于其他有
l
i
m
i
t
limit
limit限制的就使用普通的数位dp就可以了。
具体细节看代码吧.很容易T的.
#include"bits/stdc++.h"usingnamespace std;inlineintread(){int x =0;bool f =1;char c =getchar();for(;!isdigit(c); c =getchar())if(c =='-') f =0;for(;isdigit(c); c =getchar()) x =(x <<3)+(x <<1)+ c -'0';if(f)return x;return0- x;}#define SZ(x) ((int)(x.size()))#define all(x) (x).begin(),(x).end()constint maxn =3000+10;constint mod =1e9+7;constint inf =0x3f3f3f3f;using ll =longlong;int num[20], d;
ll l, r, c[22][22];
map<vector<int>, ll> dp[20][2][2];
ll comb(int n,int m){if(c[n][m]!=-1)return c[n][m];
ll ans =1;for(int i = m +1; i <= n; i++) ans *= i;for(int i =1; i <= n - m; i++) ans /= i;return c[n][m]= ans;}boolcheck(vector<int> sta){for(int i =0; i <10; i++)if(i != d && sta[i]>= sta[d])return0;return1;}
ll dfs(int pos, vector<int> sta,bool lead,bool limit){if(pos ==0)return dp[pos][lead][limit][sta]=check(sta);if(dp[pos][lead][limit].find(sta)!= dp[pos][lead][limit].end())return dp[pos][lead][limit][sta];
ll ans =0, up = limit ? num[pos]:9;if(!limit &&!lead){for(int i =0; i <= pos; i++){
vector<ll>p(20);
p[pos - i]=comb(pos, i);for(int j =0; j <10; j++){if(j == d)continue;if(sta[d]+ i <= sta[j]){
p[0]=0;break;}for(int k =0; k <= pos - i; k++){for(int m =1; m <= k; m++){if(m > sta[d]+ i - sta[j]-1)break;
p[k - m]+=comb(k, m)* p[k];}}}
ans += p[0];}}else{for(int i =0; i <= up; i++){if(!lead || i !=0) sta[i]++;
ans +=dfs(pos -1, sta, lead && i ==0, limit && i == up);if(!lead || i !=0) sta[i]--;}}return dp[pos][lead][limit][sta]= ans;}
ll solve(ll x){int tot =0;while(x){
num[++tot]= x %10;
x /=10;}
vector<int>sta(10);for(int i =0; i <10; i++) sta[i]=0;for(int i =0; i <20; i++)for(int j =0; j <2; j++)for(int k =0; k <2; k++)
dp[i][j][k].clear();returndfs(tot, sta,1,1);}intmain(){int T;memset(c,-1,sizeof(c));scanf("%d",&T);while(T--){scanf("%lld%lld%d",&l,&r,&d);printf("%lld\n",solve(r)-solve(l -1));}return0;}