Time Limit: 3000/3000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 631 Accepted Submission(s): 272
Statistic | Submit | Discuss | Note
一个数字当数位中出现次数最多的数字是z时,且唯一最多,则这个数字属于z类。
类似于数位dp,计算到某个数位,计算到当前数位有限制和非限制情况。当非限制时
后面可以任意选数字,我们可以使用指数型母函数来计算方案数。
对于这题当计算到某一位,没限制且没前导零,可以直接计算。
比如计算1000内属于1的数字,由数字正着第一位开始。
第一位选了0,不满足能直接计算的条件,继续向下展开,第二位选到0了,这个时候仍不满足直接计算条件,
继续向下展开...第二位选1的时候,这个时候满足计算条件,因为后两位可以任意选,这个时候计算已经含有1个数字1,且
后面两位任意选的方案数,通过指数型母函数可以算的此时的方案数为19种,第二位选2~9时,每一种方案都是1,共8种。
当第二位为0向下展开的时候,到了第三位,这个时候如果第三位仍然为0会继续向下展开,而第三位选1的时候
即剩余1位,要求1为众数,这个时候计算的方案数为1,第三位选2~9的方案数为0。第三位选0继续向下到第四位的
时候如果选1是一种方案,选2~9没有方案。再回到第一层,第一位选1的时候到了第二位虽然没有前导零,但是仍
有lim限制,所以继续展开,一直到最后一位的时候都没有能直接计算,到了第0位,check一下z是不是最多的数字。
总和就是19+8+1+1=29种。
这样直接计算不记忆化的话,大概是5s,记忆化的时候不需要考虑前面选的哪些数字,只需要这些的数字的个数
比如z = 3, 前面选了2个1、1个2跟前面选了1个2,2个1的计算结果一样,并不会影响到3的摆法。
存入vector,用一个map套vector就可以了,时间只需要265ms。
#include
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e3 + 500;
const int M = 4e5 + 10;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
#ifdef LOCAL
#define TIME cout << "RuningTime: " << clock() << "ms\n", 0
#else
#define TIME 0
#endif
map, ll>mp;
int x[20];
int a[20];
long double g[20], f[20];
int z;
int cnt;
long double fac[20];
void init()
{
fac[0] = 1;
for (int i = 1; i <= 18; ++i)
{
fac[i] = fac[i - 1] * i;
}
}
ll check()
{
for (int i = 0; i <= 9; ++i)
{
if (i != z)
{
if (a[i] >= a[z])
return 0;
}
}
return 1;
}
ll calc(int pos)
{
int mx = 0;
for (int i = 0; i <= 9; ++i)
{
if (i != z)
mx = max(mx, a[i]);
}
ll tot = 0;
int MI = max(mx + 1, a[z]) - a[z]; //最少摆
int MX = pos; //最多摆
for (int m = MI; m <= MX; ++m) //枚举后面pos个位置摆了多少z
{
memset(f, 0, sizeof f);
f[0] = 1;
for (int i = 0; i <= 9; ++i) //指数型母函数求解
{
memset(g, 0, sizeof g);
int begin = i == z ? m : 0; //强制z选了m个,其余的可以不选
int end = i == z ? m : min(pos - (m - a[z]), m + a[z] - a[i] - 1); //选其他的数字不能数目超过z
for (int j = 0; j <= pos; ++j)
{
for (int k = begin; k <= end && j + k <= pos; ++k)
g[j + k] += f[j] / fac[k];
}
memcpy(f, g, sizeof f);
}
tot += f[pos] * fac[pos] + 0.5; //精损
}
return tot;
}
ll dfs(int pos, int lim, int zero)
{
ll sum = 0;
if (pos == 0)
{
return check();
}
if (!lim && zero) //没限制且没前导零,可以直接计算
{
vectorv;
for (int i = 0; i <= 9; ++i) //将信息存储用于记忆化
{
if (i != z && a[i])
v.push_back(a[i]);
}
sort(v.begin(), v.end());
v.push_back(a[z]);
v.push_back(pos);
if (mp.find(v) != mp.end())
return mp[v];
return mp[v] = calc(pos);
}
int up = lim ? x[pos] : 9;
for (int i = 0; i <= up; ++i) //需要继续展开的情况
{
if (zero || i != 0) //前导零不加0的个数
a[i]++;
sum += dfs(pos - 1, lim && i == up, zero || i);
if (zero || i != 0)
a[i]--;
}
return sum;
}
ll calc(ll num)
{
cnt = 0;
while (num)
{
x[++cnt] = num % 10;
num /= 10;
}
return dfs(cnt, 1, 0);
}
int main()
{
#ifdef LOCAL
freopen("E:\\input.txt", "r", stdin);
freopen("E:\\output.txt", "w", stdout);
#endif
init();
int t;
cin >> t;
while (t--)
{
ll l, r;
scanf("%lld%lld%d", &l, &r, &z);
printf("%lld\n", calc(r) - calc(l - 1));
}
return TIME;
}