题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6796
题意:有11中类别,分别为0到10类别,如果某一个数他数中出现最多次数的是d(0<=d<=9),那这个数是d类别,比如233中3出现了2次,是最多的,所以是3类别,如果最多出现的不止一个数字,那就是10类别,比如22334,现在给你一个区间[l,r]和类别d(0<=d<=9),问你这个区间中有多少个数是d类别的。
思路:首先肯定会想到数位dp,但会发现进行记忆化搜索时那个状态不好确定。这里就可以进行魔改,当你搜索到上一个数字已经不处于边界时,知道了d数字的个数已经其他数字的个数,就可以用组合数算出来。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll a[20],b[20],c[20][20]; ll x,l,r; map<array<int,10>,ll>dp[20][2]; array<int,10>state= {0}; ll fun(int n,int m) { m=max(m,n-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; } ll dfs(int len,bool maxi,bool f) { if(dp[len][f].find(state)!=dp[len][f].end()&&maxi==0) return dp[len][f][state]; if(!len) { for(int i=0; i<10; i++) if(i!=x&&state[i]>=state[x]) return dp[len][f][state]=0; return dp[len][f][state]=1; } ll res=0,maxn=maxi?a[len]:9; if(f&&!maxi) { for(int i=0; i<=len; i++) { for(int i0=0; i0<=len-i; i0++) b[i0]=0; b[len-i]=fun(len,i); for(int i0=0; i0<10; i0++) { if(i0==x) continue; if(state[x]+i<=state[i0]) { b[0]=0; break; } for(int i1=0; i1<=len-i; i1++) { for(int i2=1; i2<=min(i1,state[x]+i-state[i0]-1); i2++) { b[i1-i2]+=fun(i1,i2)*b[i1]; } } } res+=b[0]; } return dp[len][f][state]=res; } for(int i=0; i<=maxn; i++) { if(i||f) { if(i==x||state[i]+2<state[x]+len) { state[i]++; res+=dfs(len-1,maxi&&i==a[len],1); state[i]--; } } else res+=dfs(len-1,maxi&&i==a[len],0); } return maxi?res:dp[len][f][state]=res; } ll div(ll tmp) { int p=0; while(tmp) a[++p]=tmp%10,tmp/=10; ll res=0; res+=dfs(p,1,0); return res; } int main() { memset(c,-1,sizeof(c)); int T; cin>>T; while(T--) { for(int i=0; i<20; i++) for(int i1=0; i1<2; i1++) dp[i][i1].clear(); cin>>l>>r>>x; cout<<div(r)-div(l-1)<<endl; } }