hihoCoder #1033 交错和
题目链接 #1033交错和
题目大意:
题意很清楚,就是求区间满足条件的数字的和
思路:
- 这是一道数位dp的题目
- 注意返回值,因为要算数字和,所以在进行 $dfs$ 搜索时,要返回两个值,一个是满足条件的个数 $n$ ,一个是满足条件的总和$sum$,所以当前位置的贡献就是 当前填的数乘上个数,累加取模即可。
- 要注意前导 $0$ ,因为前导 $0$ 会影响状态
- 具体可以看代码
代码:
#include <bits/stdc++.h>
using namespace std;
#define me(a, b) memset(a, b, sizeof(a))
#define IOS() ios::sync_with_stdio(false), cin.tie(0)
#define endl '\n'
#define chcek(x) x>=0?x:200-x
typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 5;
const ll mod = 1e9 + 7;
int dig[20];
ll k;
// even zero limit pos sum
pll dp[2][2][2][20][400];
bool vis[2][2][2][20][400];
ll mpow(ll x)
{
ll res = 1;
while(x) {
res *= 10;
--x;
}
return res % mod;
}
// 因为 sum 可能是负数, 所以需要加一个偏移量
// pair 第一个是满足条件的个数, 第二个是累加和
// 偶数位 前导0 上限 位置 数位和
pll dfs(int even, int zero, int limit, int pos, int sum)
{
pll ans(0, 0);
if(pos < 0) {
if(sum == k)
ans.first = 1;
return ans;
}
// 记忆化搜索
if( vis[even][zero][limit][pos][chcek(sum)] )
return dp[even][zero][limit][pos][chcek(sum)];
vis[even][zero][limit][pos][chcek(sum)] = true;
ll d = mpow(pos);
for(int i = 0; i < 10; ++i) {
// 超出界限
if(limit && i > dig[pos]) break;
pll p;
// 前导0, 偶数位就不可以改变
if(zero && !i)
p = dfs(even, zero, limit&&i==dig[pos], pos-1, sum);
else // 正常情况
p = dfs(!even, 0, limit&&i==dig[pos], pos-1, sum + (even?i:-i));
ans.first = (ans.first + p.first) % mod;
ans.second += i*d*p.first%mod + p.second;
ans.second %= mod;
}
return dp[even][zero][limit][pos][chcek(sum)] = ans;
}
ll solve(ll n)
{
me(vis, 0);
int cnt = 0;
while(n) {
dig[cnt++] = n % 10;
n /= 10;
}
return dfs(1, 1, 1, cnt-1, 0).second;
}
int main()
{
IOS();
ll l, r;
cin >> l >> r >> k;
ll ans = solve(r) - solve(l-1);
ans = (ans + mod) % mod;
cout << ans << endl;
return 0;
}
总结:
这题写了一个上午,第一就是因为前导 0 0 0 的问题,调试半天才发现,第二个就是取模的问题,在算 10 10 10 的幂次的时候,忘了取模,导致爆 l o n g l o n g longlong longlong,还是要多注意细节