定义数的消除操作为选定[L,R,x],如果数的第L到第R位上的数字都大于等于x,并且这些数都相等,那么该操作是合法的(从低位到高位编号,个位是第一位,百位是第二位……),然后将这些位数上的数减x;否则就是不合法的,不能进行操作。对一个数操作最少的次数使得这个数变成0,这个操作次数称为该数的最小操作数。如:1232的最小操作数为3,一个合法解是[2,2,1],[1,3,2],[4,4,1]。
求L~R中最小操作数为k的数的个数。
例如:132,需要操作3次才能变为0。而131131 => 111131 => 111111 =>0
单组测试数据。 三个整数L、R和k(1<=L<=R<=10^18,1<=k<=18)。
一个整数表示答案。
10 21 2
9
明显的数位dp,但是做的时候不知道如何求1到x里面符合要求的个数有多少个。
下面是官方题解:(表示维护一个栈的思路简直了,又被自己低智商给感动了)
对于一个数计算最小操作数:
维护一个栈。
从高位到低位依次考虑每一位,设当前数字为x,将栈里所有大于x的数字删除,如果此时栈里没有数字x则加入,并且答案+1。
用一个二进制数来表示栈里有哪些元素。
因为可以按位考虑所以可以用数位dp来做这个问题。
最后答案等于1~R里符合要求的数量-1~L-1里符合要求的数量。所以这里只考虑1~R的计算。
设数字R有n位,a[i]表示第i位。(从低位到高位编号,个位是第一位,百位是第二位……)
设某数第i位的数字为x,且x<a[i],且第i+1位~第n位数字与R相同,将这个数记为(i,x),
可以通过这个标准将数分类,最多18*9种分类。
对于一种分类,统计这种分类里有几个数符合题目要求。
计算出此时栈的状态和已累加的答案(即最小操作数),记为 t1,t2 , dp[t1][i−1][K−t2] 为这类里符合要求的数的数量(K为输入要求的最小操作数)。
dp[i][j][k] 表示当前栈里的元素状态为i,再填j位数字,使答案(即最小操作数)刚好再加k的方案数,这个可以通过枚举下一位数字转移得到。
总复杂度O(18*512*18*10)
代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
#pragma warning(disable:4996)
#include <fstream>
#include <iostream>
#include <functional>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <deque>
#include <ctime>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define eps 1e-10
#define LL_INF 0x3fffffffffffffff
#define INF 0x3f3f3f3f
#define mem(a, b) memset(a, b, sizeof(a))
#define pper(i,n,m) for(int i = n;i >= m; i--)
#define repp(i, n, m) for (int i = n; i <= m; i++)
#define rep(i, n, m) for (int i = n; i < m; i++)
#define sa(n) scanf("%d", &(n))
#define mp make_pair
#define ff first
#define ss second
#define pb push_back
const int maxn = 8005;
const ll mod = 1e9 + 7;
const double PI = acos(-1.0);
ll L, R, k;
ll res[20][20][1050];
int dig[20];
int change(int s, int x)
{
int i, j, k;
for (i = 0; i <= 9; i++)
{
if ((i > x) && (s >> i & 1))
{
s ^= (1 << i);
}
}
return s | (1 << x);
}
ll dfs(int len, int num, int sta,int up)
{
if (len == 0)
{
return num == k;
}
if (!up&&res[len][num][sta] != -1)
{
return res[len][num][sta];
}
int u = up == 1 ? dig[len] : 9;
int i, j, k;
ll ans = 0;
for (i = 0; i <= u; i++)
{
if (sta >> i & 1)
{
ans += dfs(len - 1, num, change(sta, i), i == u&&up);
}
else
{
ans += dfs(len - 1, num + 1, change(sta, i), i == u&&up);
}
}
if (!up)
res[len][num][sta] = ans;
return ans;
}
ll solve(ll x)
{
mem(res, 0);
int len = 0;
while (x)
{
len++;
dig[len] = x % 10;
x /= 10;
}
mem(res, -1);
return dfs(len, 0, 1, 1);
}
int main()
{
cin >> L >> R >> k;
cout << solve(R) - solve(L - 1) << endl;
return 0;
}