Find 3-friendly Integers (数位dp)

Find 3-friendly Integers

题目链接

大致题意:

求出区间[l,r]内有多少个数字满足:存在 0(不包括前导零);存在3;连续十进制位数加和整除3


解题思路:

思维题,也可以用数位dp,这里讲一下数位dp的做法

状态表示:f[20][2][2][2] 表示第pos位,余数是否为1,余数是否为2,是否能整除3的个数

分析:数位dp判断是否存在0或者3很好写,但是怎么判断十进制位中连续位上的数加和能整除3

我们用余数来表示状态,通过!(zero && i == 0)判断,可以重新从pos位开始计算余数,比如412,当到4这一位时,one=1,two=0,flag=0,当到1这一位时,one=1(因为1%3=1),two=1(因为(4+1)%3=2),flag=0

这样我们就能够实现任意连续十进制位数加和求余数

补充:这种求任意连续十进制位数的操作我还是第一次遇到,写个题解记录一下


AC代码:

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define debug(a) cout << #a << " = " << a << endl;
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
int n, m;
int a[20];
ll f[20][2][2][2];  //第pos位,余数是否为1,余数是否为2,是否能整除3的个数
int check(int state, int x) {
	return (state * 10 + x) % 3;
}
ll dfs(int pos, int one, int two, bool limit, bool zero, bool flag) {
	if (pos == -1)return flag;
	if (!limit && f[pos][one][two][flag] != -1)return f[pos][one][two][flag];
	int end = limit ? a[pos] : 9;
	ll res = 0;
	for (int i = 0; i <= end; ++i) {
		int new_one = 0, new_two = 0, new_flag = 0;
		if (one) {
			if (check(1, i) == 1)new_one = 1;
			if (check(1, i) == 2)new_two = 1;
			if (check(1, i) == 0)new_flag = 1;
		}
		if (two) {
			if (check(2, i) == 1)new_one = 1;
			if (check(2, i) == 2)new_two = 1;
			if (check(2, i) == 0)new_flag = 1;
		}
		if (!(zero && i == 0)) { //排除前导零的情况,会从pos位重新更新余数
			if (check(0, i) == 1)new_one = 1;
			if (check(0, i) == 2)new_two = 1;
			if (check(0, i) == 0)new_flag = 1;
		}
		res += dfs(pos - 1, new_one, new_two, limit && i == end, zero && i == 0, new_flag || flag);
	}
	if (!limit)f[pos][one][two][flag] = res;
	return res;
}
ll dp(ll n) {
	if (!n)return 0;
	int len = 0;
	while (n)a[len++] = n % 10, n /= 10;
	return dfs(len - 1, 0, 0, 1, 1, 0);
}
int main(void)
{
	ios::sync_with_stdio(0); cin.tie(0);
	memset(f, -1, sizeof f);
	int t; cin >> t;
	while (t--) {
		ll l, r; cin >> l >> r;
		cout << dp(r) - dp(l - 1) << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值