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;
}