AcWing 338. 计数问题(数位DP)

Posted MangataTS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AcWing 338. 计数问题(数位DP)相关的知识,希望对你有一定的参考价值。

题目链接

https://www.acwing.com/problem/content/340/

思路

我们想要快速求得 [ 1 , n ] [1,n] [1,n]内所有数的某些数字出现的次数,那么我们要从十进制数位出发,我们从左往右或者从右往左枚举每一个数位,对于当前的这个数位我们是已经定了的,所以我们就看当前这个位置的前面和后面分别有多少种枚举的方法,首先无论当前的这一位是否大于我们要计算的位值,我们都会有 ( l − 1 ) × p (l-1) \\times p (l1)×p的方案数量,如果当前计算的位值不为0的话那么说明还有前缀为0的情况,后面我们再加上当当前的这一位和我们要计算的位值相同时,前缀的选法其实已经固定了,我们只需要关注后面的选法,对于我们求的位值小于当前这一位,那么说明后面有从0选到999……种情况,否则只能从0选到后缀的情况,详情请看代码,方便理解

代码

#include<bits/stdc++.h>
using namespace std;

#define ll long long 

ll get_len(ll x)
	ll l = 0;
	while(x) x/=10,l++;
	return l;

ll pow10(ll a,ll b)
	ll ans = 1;
	while(b)
		if(b & 1) ans *= a;
		a *= a;
		b >>= 1;
	
	return ans;


ll count_num(ll n,ll loc)
	int len = get_len(n);
	ll ans = 0;
	for(int i = 1;i <= len; ++i) 
		ll p = pow(10LL,len-i);
		// p为当前遍历位次(第j位)的数大小 <10^(右边的数的位数)>, Ps:从左往右(从高位到低位)
		//l为第j位的左边的数,r为右边的数,ni为第j位上的数 
		ll l = n/p/10LL,r = n % p,ni = (n/p)%10LL;
		
		//加上前面小于前缀的所有可能
		//(1)、当i不为0时 xxx : 0...0 ~ l - 1, 即 l * (右边的数的位数) == l * p 种选法
		//(2)、当1位0时 由于不能有前导零 故xxx: 0....1 ~ l - 1, 
		//即 (l-1) * (右边的数的位数) == (l-1) * p 种选法
		if(loc) ans += l * p;
		else ans += (l - 1) * p;
		
		//加上前面等于前缀时的所有可能
		//loc > ni时 0种选法
		//loc = ni时 yyy : 0...0 ~ r 即 r + 1 种选法
		//i < dj时 yyy : 0...0 ~ 9...9 即 10^(右边的数的位数) == p 种选法时 
		//yyy : 0...0 ~ 9...9 即 10^(右边的数的位数) == p 种选法
		if(loc < ni) ans += p;
		else if(loc == ni) ans += r + 1;
	
	return ans;



int main()

	ll a,b;
	while(cin>>a>>b,a,b)
		if(a>b) swap(a,b);
		for(int i = 0;i <= 9; ++i) 
			ll r = count_num(b,i);
			ll l = count_num(a-1,i);
			cout<<(r - l)<<" \\n"[i == 9];
		
	
	
	return 0;

以上是关于AcWing 338. 计数问题(数位DP)的主要内容,如果未能解决你的问题,请参考以下文章

[计数dp] 整数划分(模板题+计数dp+完全背包变种题)

ACM - 动态规划小白入门:背包 / 线性 / 区间 / 计数 / 数位统计 / 状压 / 树形 / 记忆化 DP

AcWing 311. 月之谜 数位dp

数位DP计数问题

Acwing 1081. 度的数量(以及本人对数位dp的浅薄理解)

计数问题数位统计dp