题解 P1940 Reversible Number
传送门
题目大意:
-
求小于
10^x 并且满足“对其倒序数与其本身求和的结果每位数都是奇数”的数的数量。 -
3 \le x \le 400
题目中的例子:
而题目中的首位不能是
思路:
-
通过数学方法推导出公式。
-
因为位数
x\le 400 ,暴力高精即可。
下文将
数学部分
假设
则
为叙述方便,下列表格表示了
| [7] | [6] | [5] | [4] | [3] | [2] | [1] |
|---|---|---|---|---|---|---|
显然,第
温馨提示:请注意区分 sum(n) 的第 i 位和 [i ](即第 i 项)之间的区别。
首先,为使第 1 位(从右往左)为奇数,[1] 中必须为奇数。考虑到进位对其他位的影响,分为两种情况:大于
1、[1] 中是大于 10 的奇数
此时,[1](
| [7] | [6] | [5] | [4] | [3] | [2] | [1] |
|---|---|---|---|---|---|---|
| 15 | ? | ? | ? | ? | ? | 15 |
这里,[6] 是不能进位的,否则会使
| [7] | [6] | [5] | [4] | [3] | [2] | [1] |
|---|---|---|---|---|---|---|
| 15 | 8 | ? | ? | ? | 8 | 15 |
同理,[5] 必须进位,而 [3] 必须是奇数,那么就是进位的奇数,可以发现与 [1]、[7] 符合的条件相同,即产生了循环。那么剩下的过程就可以递归求解。
但由于
1°
以
| [4] | [3] | [2] | [1] |
|---|---|---|---|
| 15 | 8 | 8 | 15 |
此时第 3 位是偶数,不满足题设。
2°
以
| [5] | [4] | [3] | [2] | [1] |
|---|---|---|---|---|
| 15 | 8 | 11 | 8 | 15 |
此时 [3] 是奇数,而 [3]
3°
以
| [6] | [5] | [4] | [3] | [2] | [1] |
|---|---|---|---|---|---|
| 15 | 8 | 11 | 11 | 8 | 15 |
此时第 4 位是偶数(
4°
以
| [7] | [6] | [5] | [4] | [3] | [2] | [1] |
|---|---|---|---|---|---|---|
| 15 | 8 | 11 | 6 | 11 | 8 | 15 |
此时是可以满足题设的。(与上表对应的
而通过枚举数学方法得知,[1] 与 [7] 对应的
故一共有
2、[1] 中是小于 10 的奇数
此时,[1] 可以取
| [7] | [6] | [5] | [4] | [3] | [2] | [1] |
|---|---|---|---|---|---|---|
| 7 | ? | ? | ? | ? | ? | 7 |
[6] 不能进位,否则会使第 7 位成为偶数。而由第 2 位为奇数知,[2] 内一定是奇数。故是不进位的奇数,与 [1]、[7] 符合条件相同。即再次产生了循环。
当然,
当
| [4] | [3] | [2] | [1] |
|---|---|---|---|
| 5 | 3 | 3 | 5 |
类似地,可以推出 [1] 与 [4] 对应的
高精部分
由以上的数学推论,可以知道我们只需要写出高精乘低精、高精加高精(统计答案)即可。具体写法可以参考代码部分。
#include<bits/stdc++.h>
using namespace std;
const int N=400+20;
struct BigInt {
int a[N], len;
inline void carry() { //进位,顺便更新 len
for(int i=0; i<len; i++) {
a[i+1]+=a[i]/10, a[i]%=10;
if(a[len]) ++len; //更新 len
}
}
BigInt operator = (int i2) { //将低精赋值给高精
for(int i=1; i<=N-3; i++) a[i]=0; //清零
len=1, a[0]=i2, carry();
}
BigInt operator * (int i2) { //高精乘低精
BigInt ret=*this;
for(int i=0; i<ret.len; i++) ret.a[i]*=i2;
ret.carry();
return ret;
}
BigInt operator + (BigInt b2) { //高精加高精
BigInt ret=*this;
ret.len=max(ret.len, b2.len);
for(int i=0; i<ret.len; i++) ret.a[i]+=b2.a[i];
ret.carry();
return ret;
}
inline void output() { //输出
for(int i=len-1; i>=0; i--) printf("%d", a[i]);
}
} ans;
int x;
int main() {
ans=0;
scanf("%d", &x);
for(int i=1; i<=x; i++) { //从 1 到 x 都要计算
if(i%2==0) { //f(n)%2==0
BigInt dl;
dl=20;
for(int t=1; t<=(i-2)/2; t++) dl=dl*30; //(i-2)/2 次幂
ans=ans+dl;
} else if(i%4==3) { //f(n)%4==3
BigInt dl;
dl=20*5;
for(int t=1; t<=(i-3)/4; t++) dl=dl*(20*25); //(i-3)/4 次幂
ans=ans+dl;
}
}
ans.output();
return 0;
}