题目
http://acm.hdu.edu.cn/showproblem.php?pid=6796
题意
一个数字的值为该数字位数上的众数,如果位数上的众数有多个,则值为10。问 l 到 r 区间内值为 d 的数字有多少个。
题解
一眼看去非常明显的数位dp题,首先我们考虑当 limit 限制为真时dp数组是不起作用的,所以只用正常进行搜索就好。重点是当 limit 限制为假时,dp数组不好找到共性。
我们举个例子,当总数为 123345,所要找的值是 6。我们枚举到 121XXX(X为还枚举的数位)时,可以发现后三个数位可以填的是 0 ~ 999 之间的所有数。因为最后要让 6 成为众数,那么其实就是在已经有 2 个 1 和 1 个 2 以及还有 3 个要填的位置的情形下求出使 6 成为众数的情况有多少种,这时候可以通过暴力(稍稍带点dp =v=)的方式(在下面 calc 函数中)求出。这时候我们突然发现(阿巴阿巴)前三个数枚举到 122XXX 和 121XXX 对于 6 来说都是 1 个不是 6 的数 和 2 个不是 6 的数,我们并不需要去知道他是什么数,那么 122XXX 和 121XXX 就拥有了共性。所以我们可以通过把 0 ~ 9 的数量排个序之后将这两种情况作为一个字符串进行表示。这里注意一点,当前要求的值不能混在里面排序,所以可以先把除了这个值的所有值进行排序后再将该值放到字符串最后。
#include <bits/stdc++.h>
// #include <iostream>
// #include <cstring>
// #include <string>
// #include <algorithm>
// #include <cmath>
// #include <cstdio>
// #include <queue>
// #include <stack>
// #include <map>
// #include <bitset>
// #include <set>
// #include <vector>
// #include <iomanip>
#define ll long long
#define ull unsigned long long
#define met(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define bep(i, a, b) for(int i = a; i >= b; --i)
#define lowbit(x) (x&(-x))
#define MID (l + r) / 2
#define ls pos*2
#define rs pos*2+1
#define pb push_back
#define ios() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int maxn = 1e5 + 1010;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const double eps = 1e-4;
const double PI = acos(-1);
int num[10];
ll hs[10], cnt;
ll arr[20], tail;
map<string, ll> dp[20];
ll dpr[20];
ll c[20][20];
ll C(int n, int m) {
if(m < n - m) m = n - m;
if(c[n][m]) return c[n][m];
ll res = 1;
rep(i, 1, m) res *= (n - i + 1);
rep(i, 1, m) res /= i;
return c[n][m] = res;
}
string Hash(int x) {
string str = "";
cnt = 0;
rep(i, 0, 9) {
if(i == x) continue;
hs[cnt++] = num[i];
}
sort(hs, hs + cnt);
hs[cnt++] = num[x];
rep(i, 0, 9) str += hs[i] + 'a';
return str;
}
ll calc(int pos, int x) {
ll res = 0;
rep(i, 0, pos) {
rep(j, 0, pos - i)
dpr[j] = 0;
dpr[pos - i] = C(pos, i);
rep(j, 0, 9) {
if(j == x)
continue;
if(num[x] + i <= num[j]) {
dpr[0] = 0;
break;
}
rep(k, 0, pos - i) {
rep(w, 1, min(k, num[x] + i - num[j] - 1)) {
dpr[k - w] += C(k, w) * dpr[k];
}
}
}
res += dpr[0];
}
return res;
}
ll dfs(ll pos, int limit, int zero, int x) {
string str = Hash(x) + (char)(zero + '0');
if(!limit && dp[pos].find(str) != dp[pos].end())
return dp[pos][str];
if(!pos) {
rep(i, 0, 9) {
if(i == x)
continue;
if(num[i] >= num[x])
return dp[pos][str] = 0;
}
return dp[pos][str] = 1;
}
ll res = 0;
int up = limit ? arr[pos] : 9;
if(!limit && !zero) {
return dp[pos][str] = calc(pos, x);
}
else {
rep(i, 0, up) {
if(!i && zero)
res += dfs(pos - 1, limit && i == up, 1, x);
else {
if(i == x || num[i] + 1 <= num[x] + pos - 1) {
num[i]++;
res += dfs(pos - 1, limit && i == up, 0, x);
num[i]--;
}
}
}
if(!limit) dp[pos][str] = res;
return res;
}
}
ll solve(ll num, ll x) {
rep(i, 0, 19) dp[i].clear();
tail = 0;
while(num) {
arr[++tail] = num % 10;
num /= 10;
}
return dfs(tail, 1, 1, x);
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
ll l, r, d;
scanf("%lld%lld%lld", &l, &r, &d);
printf("%lld
", solve(r, d) - solve(l - 1, d));
}
return 0;
}