数位DP

数位DP

338. 计数问题 - AcWing题库

思路分析:首先可以用前缀和的思想,count(i, j) 表示在1 - i中j出现的次数

则a 到 b数字j出现的次数则为count(b, j) - count(a - 1, j)

那么count函数该怎么写呢?我们假设要求数字x在1 - n中第i位出现的次数

首先假设x在第4位,然后n拆开成每一位可以表示成abcdefg,

那么1 - n中的满足条件的数字可以表示成mmmxnnn

\(1 \leq mmmxnnn \leq abcdefg\)

接下来我们进行分类讨论:

1、mmm < abc

那么mmm可以取值的范围是0 - abc - 1,有abc种

而x右边的部分可以取值的范围是0 - 1000, 也就是10的x - 1位数 - 1次方

但要注意的是如果x = 0,则mmm应该从001开始,因为不能前面全为0

因此总数为(abc - !x) * 1000

2、mmm = abc

此时要再次进行分类讨论:

(1)d == x

此时左边不需要分析,右边可取的范围为0 - efg

总数为efg + 1

(2)d > x

同样只要分析右边,右边可取的范围是0 - 999

总数为10的x位数 - 1次方

每一位同理即可,如果x = 0那么从次高位开始遍历,因为最高位不能为0

代码:

//#pragma comment(linker,   "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);++i)
#define per(i,b,a) for (int i=(b);i>=(a);--i)
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
#define mp make_pair
#define MOD 1000000007
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef set<int> si;
typedef set<ll> sll;
const pii moves[] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
ll ksm(ll a, ll b, ll p) {if (b == 0) return 1; ll ns = ksm(a, b >> 1, p); ns = ns * ns % p; if (b & 1) ns = ns * a % p; return ns;}		
const int MAXN = 0x7fffffff;

int get(int l, int r, vector <int> &dig)
{
	int res = 0;
	for(int i = l; i >= r; i --)
		res = res * 10 + dig[i];
	return res;
}//取得l-r表示的数字

int pow10(int x)
{
	int res = 1;
	while(x --)
		res *= 10;
	return res;
}//10的x次方

int count(int num, int x)
{
	vii dig;
	while(num)
	{
		dig.push_back(num % 10);
		num /= 10;
	}
	int n = dig.size();
	int res = 0;
	for(int i = n - 1 - !x; i >= 0; i --)
	{
		if(i < n - 1)
		{
				res += get(n - 1, i + 1, dig) * pow10(i); //位数从0开始所以不用i - 1
			if(!x)//如果x是0,那么x左边可取的范围从1开始,否则从0开始,所以x = 0时次数要 - 1
				res -= pow10(i);//位数从0开始所以不用i - 1
		}
		// cout << res << endl;
		if(dig[i] == x)
			res += get(i - 1, 0, dig) + 1;
		else if(dig[i] > x)
			res += pow10(i);//位数从0开始所以不用i - 1
	}
	return res;
}


int main ()
{	
	//IOS;
	int a, b;

	while(cin >> a >> b, a || b)
	{
		if(a > b) swap(a, b);
		for(int i = 0; i < 10; i ++)
			cout << count(b, i) - count(a - 1, i) << " ";
		cout << endl;
	}
	return 0;
}	
/*

*/
posted @ 2022-01-11 18:18  Yra  阅读(67)  评论(0)    收藏  举报