input
3
1 10 1
1 11 1
1 100 0
output
1
2
1
数位DP+组合数学
题意:找到[l,r]符合条件[=d]的数字。[=d]定义为:当前数字中d出现的次数严格最大。e.g.111223为[*=1],112233为啥也不是
思路:典型的数位dp虽然想到了,,,但没想到组合数学
数位dp:dp[][][]三个维度分别表示当前还剩pos位,题意中的d,和一个长度为10的数组表示pos之前出现的每个数字的次数,这题不仅需要考虑limit还需要考虑前导0这个限制
组合数学:这个还是欠考虑,也不会,当在没有限制的情况下,比如1234XXX。这时不需要在dfs下去这种情况可以通过组合数学判断出来,方法如下:先判读至少还需要添加st个数字d。当前的情况数就是C(pos,st)*calc(pos-st,up)。
而calc计算的则是剩下n个位置,每个数字最多使用up次的种类数
ll calc(int n,int d,int up){
mem(f,0);
f[0][0]=1;
for(int i=1;i<=10;i++){
if(i==d+1) for(int j=0;j<=n;j++) f[i][j]=f[i-1][j];
else
for(int j=0;j<=n;j++){
for(int k=0;k<=j;k++)
if(j-k+arr[i-1]<=up)//新增填入j-k个数字[i-1],加上原有的需要<=限制数up
f[i][j]+=f[i-1][k]*C[j][j-k];//前i-1个数字填满k个位置,剩下的j-k个位置为组合数C(j,j-k)
}
}
return f[10][n];
}
完整代码
#include
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define fcout cout<
using namespace std;
typedef long long ll;
//======================================
namespace FastIO{
char print_f[105];void read() {}void print() {putchar('\n');}
template <typename T, typename... T2>
inline void read(T &x, T2 &... oth){x = 0;char ch = getchar();ll f = 1;while (!isdigit(ch)){if (ch == '-')f *= -1;ch = getchar();}while (isdigit(ch)){x = x * 10 + ch - 48;ch = getchar();}x *= f;read(oth...);}
template <typename T, typename... T2>
inline void print(T x, T2... oth){ll p3=-1;if(x<0) putchar('-'),x=-x;do{print_f[++p3] = x%10 + 48;}while(x/=10);while(p3>=0) putchar(print_f[p3--]);putchar(' ');print(oth...);}} // namespace FastIO
using FastIO::print;
using FastIO::read;
//======================================
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn = 1e6+5;
map<array<int,10>,ll>dp[20][20];
array<int,10>arr;
int a[25];
ll f[25][25],C[25][25];
int check(int d){
for(int i=0;i<10;i++){
if(i!=d&&arr[i]>=arr[d]) return 0;
}
return 1;
}
ll calc(int n,int d,int up){
mem(f,0);
f[0][0]=1;
for(int i=1;i<=10;i++){
if(i==d+1) for(int j=0;j<=n;j++) f[i][j]=f[i-1][j];
else
for(int j=0;j<=n;j++){
for(int k=0;k<=j;k++)
if(j-k+arr[i-1]<=up)//新增填入j-k个数字[i-1],加上原有的需要<=限制数up
f[i][j]+=f[i-1][k]*C[j][j-k];//前i-1个数字填满k个位置,剩下的j-k个位置为组合数C(j,j-k)
}
}
return f[10][n];
}
ll dfs(int pos,int d,bool lead,bool limit){
if(!limit&&!lead&&dp[pos][d].count(arr)) return dp[pos][d][arr];
if(pos==0) return dp[pos][d][arr]=check(d);
if(!lead&&!limit){
int maxx=0;
for(int i=0;i<10;i++) if(i!=d) maxx=max(maxx,arr[i]+1); //d需要大于等于maxx
int st=max(maxx-arr[d],0);//pos后至少还需要st个d
if(st>pos) return dp[pos][d][arr]=0;//需要add的d大于pos为0
ll res=0;
for(int i=st;i<=pos;i++){
res+=C[pos][i]*calc(pos-i,d,arr[d]+i-1);
}
return dp[pos][d][arr]=res;
}
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++){
if(!(i==0&&lead)) arr[i]++;
ans+=dfs(pos-1,d,lead && i==0,limit && i==a[pos]);
if(!(i==0&&lead)) arr[i]--;
}
if(!limit&&!lead) dp[pos][d][arr]=ans;
return ans;
}
ll solve(ll x,int d){
int pos=0;
while(x){
a[++pos]=x%10;
x/=10;
}
return dfs(pos,d,1,1);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("H:\\code\\in.in", "r", stdin);
freopen("H:\\code\\out.out", "w", stdout);
clock_t c1 = clock();
#endif
//**************************************
for(int i=0;i<=20;i++) C[i][0]=1;
for(int i=1;i<=20;i++){ //预处理组合数
for(int j=1;j<=i;j++){
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
}
int T;
read(T);
while(T--){
ll l,r,d;
read(l,r,d);
cout<<solve(r,d)-solve(l-1,d)<<"\n";
}
//**************************************
#ifndef ONLINE_JUDGE
cerr << "Time:" << clock() - c1 << "ms" << endl;
#endif
return 0;
}