题目链接:http://oj.lovelyanqi.com/problem/L04 K
想知道 ([0, x]) 之内有多少个回文数,
假设 (x) 有 (c) 位,我们可以先预处理出前 (c - 1) 位内 ((99....99)) 的答案
然后再加上 (10^{c-1}) 到 (x) 内的答案即可
考虑如何统计,需要对 (x) 的数位分奇偶性讨论
如果数位是奇数
例如 (12345) ,则前半部分为 ([10,12])
以第三位的数为中间点,如果前半部分 (left_x) 为 ([10,11]), 则中间数位可以为 ([0,9]) 的任意数,
如果 (left_x) 为 (12), 则中间数位只能为 ([0,3]) 之间的数,否则就会大于 (x)
偶数的情况类似,不过没有了中间点
重头戏来了
如果为奇数
设(lx)为前半部分的数, (M) 为中间位置的数, (rx) 为 (lx) 倒置过来的数 ((12345 -> 54321)), 记为 (rx = reverse(lx))
先考虑 (lx < left_x) 的情况:
当前半部分小于 (left_x) 时,形如 (lx M rx) 的都为合法答案,其中 (lx < left_x, M 属于 [0,9], rx = reverse(lx))
分别考虑 (lx , M ,rx) 的贡献,(lx) 贡献了 (sumlx * 10^{c / 2}),
设 (rsum) 表示所有合法 (rx) 的和,则 (sumx) 贡献了 $ 10 $ 次 (M = [0,9])都贡献一次,即为 $ M * rsum $.
再考虑 (lx = left_x) 的情况:
当 (M < M_x) 时,(rsum_x) 贡献了 (M) 次,
当 (M = M_x) 时,需要比较 (reverse(rx)) 和 (lx) 的大小关系,
如果 (reverse(rx) > lx), 则 (left_x M_x reverse(left_x)) 为合法回文数,需要加上其自身的贡献
否则不合法,不需要算贡献
偶数的情况类似,不过要比奇数的情况简单很多,可以自己尝试推一下
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1000010;
int T, a, b;
ll sum[maxn], rsum[10010], po[15];
ll S[15] = {1, 45, 540, 50040, 545040, 50045040, 545045040, 50045045040, 545045045040, 50045045045040};
int f[15];
int rev(int x){
int res = 0;
int cnt = 0;
int tmp = x;
while(tmp){
f[++cnt] = tmp % 10;
tmp /= 10;
}
for(int i = 1 ; i <= cnt ; ++i){
res += f[i] * po[cnt - i];
}
return res;
}
ll calc(int x){
ll res = 0;
int cnt = 0;
int tmp = x;
while(tmp){
++cnt;
tmp /= 10;
}
res = 1ll * (po[cnt - 1] + x - 1) * (x - po[cnt - 1]) / 2;
return res;
}
void init(){
for(int i = 1 ; i <= 9999 ; ++i){
rsum[i] = rsum[i - 1] + rev(i);
}
for(int i = 1 ; i <= 99999 ; ++i){
sum[i] = calc(i);
}
}
ll solve(int x){
if(x == -1) return 0;
ll res = 0;
int cnt = 0;
int tmp = x;
while(tmp){
++cnt;
tmp /= 10;
}
int lx = x / po[cnt / 2], rx = x % po[cnt / 2], rlx = rev(lx) % po[cnt / 2] ;
res = S[cnt - 1];
res += 1ll * sum[lx] * po[cnt / 2];
if(cnt % 2 == 0){
res += rsum[lx - 1] - rsum[po[cnt / 2 - 1] - 1];
if(rlx <= rx){
res += 1ll * lx * po[cnt / 2] + rlx;
}
} else{
int plx = lx / 10, rem = lx % 10;
res += 1ll * 10 * ( rsum[plx - 1] - rsum[po[cnt / 2 - 1] - 1]);
if(rev(plx) <= rx){
res += 1ll * rem * ( rsum[plx] - rsum[plx - 1]);
res += 1ll * lx * po[cnt / 2];
res += rsum[plx] - rsum[plx - 1]; // 给新加的加上
} else{
res += 1ll * rem * ( rsum[plx] - rsum[plx - 1]);
}
}
return res;
}
ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
po[0] = 1;
for(int i = 1 ; i <= 10 ; ++i) po[i] = 1ll * po[i - 1] * 10;
init();
T = read();
while(T--){
a = read(), b = read();
if(a == 1000000000) --a;
if(b == 1000000000) --b;
ll ansa = 0, ansb = 0 ;
if(a - 1 < 10){
for(int i = 1 ; i <= a - 1 ; ++i) ansa += i;
} else{
ansa = solve(a - 1);
}
if(b < 10){
for(int i = 1 ; i <= b ; ++i) ansb += i;
} else{
ansb = solve(b);
}
printf("%lld
", ansb - ansa);
}
return 0;
}