由于赛时不会写,补题时也花了较大功夫才弄懂,特写一篇题解来加深理解
Problem Statement
A non-negative integer
X
X
X is called a palindrome number if its decimal representation (without leading zeros) is a palindrome.
For example,
363
363
363,
12344321
12344321
12344321, and
0
0
0 are all palindrome numbers.
Find the N N N-th smallest palindrome number.
题意
你需要输出第N大的回文数。
分析
我们先写出几项找找规律,设回文数长度为
d
d
d
d
=
1
d = 1
d=1,9
(
1
−
−
−
9
)
(1 --- 9)
(1−−−9)
d
=
2
d = 2
d=2,9
(
11
−
−
−
99
)
(11 --- 99)
(11−−−99)
d
=
3
d = 3
d=3,90
(
11
−
−
−
99
)
(11 --- 99)
(11−−−99)
d
=
4
d = 4
d=4,90
d
=
5
d = 5
d=5,900
我们发现长度为d的回文数,只有
9
×
1
0
(
d
+
1
)
/
2
−
1
9 \times 10^{(d + 1)/2 - 1}
9×10(d+1)/2−1个。有了这个重要结论,我们就可以缩小问题了。现在需要求第
N
N
N大的数,我们枚举第N大的数的长度
l
e
n
len
len,当
N
N
N >
9
×
1
0
(
l
e
n
+
1
)
/
2
−
1
9 \times 10^{(len + 1)/2 - 1}
9×10(len+1)/2−1,说明
N
N
N的长度大于
l
e
n
len
len,我们将
N
−
9
×
1
0
(
l
e
n
+
1
)
/
2
−
1
N - 9 \times 10^{(len + 1)/2 - 1}
N−9×10(len+1)/2−1,这样就缩小了问题。当
N
N
N <
9
×
1
0
(
l
e
n
+
1
)
/
2
−
1
9 \times 10^{(len + 1)/2 - 1}
9×10(len+1)/2−1时,我们知道
l
e
n
len
len长度的最小回文数总是
1000001
1000001
1000001这样的形式的,由于是回文的我们只看前半数位,假设当前求第
N
N
N大的数,我们拿出前半数位,前半数位的数加上
N
N
N就是这个所求回文数的前半数位了,将前半数位翻转加上去就是答案了。由于数可能会超过LL,我们使用字符串模拟的做法。注意最后求答案时,对于奇数长度的数,我们拼接前后半段时,需要将前半段的末尾去掉,保证是一个奇数长度的数。
#define int long long
#define ios ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
const int N = 1e5+5;
LL n;
long long TEN(int x) { // 求10的x次的递归函数,使用pow也可
return x == 0 ? 1 : TEN(x - 1) * 10;
}
void solve(){
cin >> n;
if(n == 1) {
cout << 0 << endl;
return ;
}
n--;
for(int p = 1; ; p++){
LL d = (p + 1)/2;
LL k = TEN(d - 1);
if(n > k * 9){
n -= k * 9;
}else{
string s = to_string(TEN(d - 1) + n - 1); //数字变字符串操作
string t = s;
reverse(t.begin(), t.end());
if(p % 2 == 1) s.pop_back(); // 特判奇数长度
s = s + t;
cout << s << endl;
return ;
}
}
}