题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=244&page=show_problem&problem=3364
设 (dp[i][j][0/1][0/1][0/1/2][0/1/2]) 表示当前是原数的第 (i) 位,翻转后是翻转数的第 (j) 位(因为有前导零,所以位置可能不变),原数卡不卡下界,原数卡不卡上界,前 (i) 位的原数翻转后小于/等于/大于后 (j) 位的上/下界
转移分类讨论即可,其中翻转后数字的大小关系,是从低位向高位维护的
注意原数前导零和最后连续的零翻转后的变化,小心处理即可,还要使用 (unsigned long long)
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
typedef unsigned long long ull;
const int maxn = 70;
int T;
ull A, B;
ull dp[maxn][maxn][2][2][3][3];
int a[maxn], b[maxn];
int ca, cb;
ull dfs(int pos, int rpos, int llx, int lls, int lrx, int lrs, int lead){ // llx 原数下界, lls 原数上界, lrx 翻转数下界, lrs 翻转数上界
if(pos == 0) {
int fx = 0, fs = 0;
if(rpos-1 >= ca && lrx >= 1) fx = 1;
if(rpos-1 < cb || (rpos-1 == cb && lrs <= 1)) fs = 1;
if(fx && fs) return 1;
else return 0;
}
if(!lead && dp[pos][rpos][llx][lls][lrx][lrs] != -1) return dp[pos][rpos][llx][lls][lrx][lrs];
int lmx = llx ? a[pos] : 0;
int lms = lls ? b[pos] : 9;
ull res = 0;
for(int i = lmx ; i <= lms ; ++i){ // 枚举原数第 i 个位置
int ns, nx;
if(i > b[rpos]){ // 新的位置的数大于翻转数上界
ns = 2;
} else if(i == b[rpos]){ // 等于
ns = lrs;
} else{ // 小于
ns = 0;
}
if(i < a[rpos]){ // 新的位置小于翻转数下界
nx = 0;
} else if(i == a[rpos]){
nx = lrx;
} else{
nx = 2;
}
if(lead && i == 0) { // 前导 0, 并且满足下界条件
res += dfs(pos-1, rpos, 1, 0, 1, 1, 1);
}
else res += dfs(pos-1, rpos+1, llx && (i == lmx), lls && (i == lms), nx, ns, 0);
}
if(!lead) dp[pos][rpos][llx][lls][lrx][lrs] = res;
return res;
}
ull solve(){
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(dp, -1, sizeof(dp));
ull tmp = A;
ca = 0, cb = 0;
while(tmp){
a[++ca] = tmp % 10;
tmp /= 10;
}
tmp = B;
while(tmp){
b[++cb] = tmp % 10;
tmp /= 10;
}
return dfs(cb, 1, 1, 1, 1, 1, 1);
}
int main(){
int T;
scanf("%d", &T);
for(int kase = 1 ; kase <= T ; ++kase){
scanf("%llu%llu", &A, &B);
printf("Case %d: %llu
", kase, solve());
}
return 0;
}