NOI 模拟赛
T1 手机号码
求有多少 $L,R$ 之间的 $11$ 位数同时满足
1. $4$ 和 $8$ 不同时出现
2.有三连相同数字
$L,R$ 都是 11 位数
sol:
数位 dp
记状态为 第 $i$ 位,前一位是 $pre$ ,前一位和前前一位是否相同,是否有 $4$ ,是否有 $8$ ,是否计入答案
然后搜索就完事了
考场上三个大锅
1.$L,R$ 虽然都是 $11$ 位数,但 $L-1$ 不一定是 $11$ 位数
2.竟然访问了数组的 $-1$ 。。。(“没有前一位”的状态我标的是 $-1$,其实用 $10$ 就可以了)
3.不管搜索的时候有没有 $lim$,直接就把 dp 清零了,竟然没出锅...
#include <bits/stdc++.h> #define int long long #define LL long long #define rep(i, s, t) for (register LL i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register LL i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch; for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } int dig[30]; LL dp[50][12][2][2][2][2]; LL dfs(LL pos, LL pre, LL ispre, LL if4, LL if8, LL lim, LL mz) { if(pos == 0) return mz; if(!lim && (~dp[pos][pre][ispre][if4][if8][mz])) return dp[pos][pre][ispre][if4][if8][mz]; dp[pos][pre][ispre][if4][if8][mz] = 0; LL res = 0, Bound = lim ? dig[pos] : 9; rep(i, 0, Bound) { if(((i == 0) && (pos == 11)) || (if4 && (i == 8)) || (if8 && (i == 4))) continue; res += dfs(pos - 1, i, (i == pre), if4 || (i == 4), if8 || (i == 8), lim && (i == Bound), mz || ((i == pre) && ispre)); } if(!lim) dp[pos][pre][ispre][if4][if8][mz] = res; return res; } LL L, R; LL solve(LL x) { int num = 0; memset(dp, -1, sizeof(dp)); memset(dig, 0, sizeof(dig)); while(x) { dig[++num] = x % 10; x = x / 10; } return dfs(11, -1, 0, 0, 0, 1, 0); } signed main() { cin >> L >> R; //LL x = solve(L - 1), y = solve(R); cout << solve(R) - solve(L - 1) << endl; }
T2 补退选
不写了,弱智题,空间开小了 10 倍都能过
T3 猪国杀