这题如果普通数位dp,状态不好表示,但是我们发现,一旦当dp过程中,脱离了被最高项束缚的状态后,后面的数字就可以随便填
因此我们直接用组合数dp计算,首先维护前面出现的数字的个数,之后枚举答案d出现的合法状态的次数
设计dp[][],表示前i位,不包括d,在剩余的个数中已经存了j的答案。因此我们可以枚举i后再枚举k吗,计算这个位选k次的答案。
#include<bits/stdc++.h> #define rep(i,x,n) for(int i=x;i<=n;i++) using namespace std; typedef long long ll; typedef pair<int,ll> pll; const int N=5e4+10; int c[1000][1000]; int s[N]; int d; int len; ll dp[25][25]; int cnt[N]; ll l,r; ll dfs(int pos,int limit,int lead){ if(pos==-1){ int mx=0,num=0; rep(i,0,9) if(cnt[i]>cnt[mx]) mx=i; rep(i,0,9) if(cnt[i]==cnt[mx]) num++; return mx==d&&num==1; } if(!limit&&!lead){ ll ans=0; int mx=cnt[d]; rep(i,0,9) if(i!=d) mx=max(mx,cnt[i]+1); for(int num=mx;num<=cnt[d]+pos+1;num++){ memset(dp,0,sizeof(dp)); dp[0][0]=1; rep(i,1,10){ if(i==d+1){ for(int j=0;j<=20;j++) dp[i][j]=dp[i-1][j]; continue; } for(int j=0;j<=cnt[d]+pos+1-num;j++){ for(int k=0;k<=num-cnt[i-1]-1&&j-k>=0;k++){ dp[i][j]+=dp[i-1][j-k]*c[pos+1-j+k][k]; } } } ans+=dp[10][cnt[d]+pos+1-num]; } return ans; } int up=limit?s[pos]:9; ll ans=0; for(int i=0;i<=up;i++){ if(!lead||i!=0) cnt[i]++; ans+=dfs(pos-1,limit&&i==s[pos],lead&&i==0); if(!lead||i!=0) cnt[i]--; } return ans; } void init(){ int i,j; c[0][0]=1; for(i=1;i<=20;i++){ c[i][0]=1; for(j=1;j<=i;j++){ c[i][j]=c[i-1][j]+c[i-1][j-1]; } } } ll solve(ll x){ int len=0; if(x==0){ s[0]=0; len=1; } while(x){ s[len++]=x%10; x/=10; } return dfs(len-1,1,1); } int main(){ ios::sync_with_stdio(false); init(); int t; cin>>t; while(t--){ cin>>l>>r>>d; cout<<solve(r)-solve(l-1)<<endl; } }