X Number
Time Limit: 3000/3000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 805 Accepted Submission(s): 327
There are 11 categories, numbered from 0 to 10. For each positive integer x, if there exists only one type of digit d (0≤d≤9) that occurs in x with the highest frequency, then we say x should be classified into category d, or otherwise, in case such digit doesn't exist, we say x should be classified into category 10.
For example,
● 111223 should be classified into category 1 since digit 1 occurs three times, and digits 2 and 3 occur less than three times respectively, and
● 3345544 should be classified into category 4 since digit 4 occurs three times, and digits 3 and 5 occur less than three times respectively, and
● 112233 should be classified into category 10 since digits 1, 2 and 3 occur twice respectively.
Little W doesn't care about category 10 and he just wants Teitoku to tell him the number of integers ranged from l to r that should be classified to another category d. However, Teitoku can hardly solve this problem, so he asks you for help.
The first line contains an integer T (1≤T≤1000), denoting the number of test cases. Then follow all the test cases.
For each test case, the only line contains three integers l, r and d (1≤l≤r≤1018,0≤d≤9), representing a problem.
For the sample cases, 1 and 11 are in category 1, 100 is in category 0 and 10 is in category 10.
题意:
给你三个整数l,r,d(1<=l<=r<=1e18,1<=d<=9),求在区间[l,r]中,有多少个数字,其数位上数码d出现的次数严格最多(不包含前导0)。
思路:
一看这个数据范围还有问题显然是数位dp。
但是这题,我们显然状态设计中不能直接存储0~9每个数字出现的次数。
考虑limit特性,当前导0去掉后,并且没有limit限制,那么后面的数位都可以填0~9任意一个数字。
那么我们就可以从这里下手,考虑如何快速计算出没有前导0和limit限制下的答案。
考虑枚举数码d最终出现了num次,其他数码i已经出现了cnt[i]次,然后我们就可以对每个num做一个dp,dp[i][j]表示填到第i个数码,剩下的位置中已经填了j个数字的答案总数(这样就能枚举当前数码i填了剩下的位置中k个位置,用组合数转移一下)。
这样每次都累加答案,就可以快速计算出没有限制时的答案。至于边界条件,留给读者自行推导。
状态转移公式:
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int maxn=4e1+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
ll l, r, d;
int a[maxn];
template <typename T>
inline void read(T &X){
X=0;int w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
if(w) X=-X;
}
ll dp[22][22];
ll c[maxn][maxn];
int cnt[maxn];
ll dfs(int pos,int lmt,int qd){
if(pos==-1) {
rep(i,0,9) {
if(i!=d&&cnt[i]>=cnt[d])
return 0;
}
return 1;
}
if(!lmt&&!qd){
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++){//cnt[d]
memset(dp, 0, sizeof(dp));//
dp[0][0] = 1;//
for (int i = 1; i <= 10;i++){
if(i-1==d){
rep(j, 0, 20) dp[i][j] = dp[i - 1][j];
continue;
}
for (int j = 0; j <= cnt[d] + pos + 1 - num;j++){
for (int k = 0; k <= j && k <= num - cnt[i - 1] - 1; 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 = lmt ? a[pos] : 9;
ll ans = 0;
rep(i,0,up){
if(!qd||i)
cnt[i]++;
ans += dfs(pos - 1, lmt && i == a[pos], qd && i == 0);
if(!qd||i)
cnt[i]--;
}
return ans;
}
ll cal(ll x){
int len = 0;
while(x){
a[len++] = x % 10;
x /= 10;
}
return dfs(len - 1, 1, 1);
}
void solve(){
read(l);
read(r);
read(d);
ll ans = cal(r) - cal(l - 1);
printf("%lld\n", ans);
}
int main(){
/*
#ifdef ONLINE_JUDGE
#else
freopen("D:/Temp/in.txt", "r", stdin);
#endif
*/
// freopen("e://duipai//myout.txt","w",stdout);
int T=1,cas=1;
read(T);
c[0][0] = 1;
rep(i,1,25){
c[i][0] = 1;
rep(j, 1, i) c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
}
//cout << c[4][2] << endl;
while(T--){
solve();
}
system("pause");
return 0;
}