前言
老感觉栋栋的程序有点迷…怎么只枚举到1e7
题目大意
给你一个数d,要你找出有多少个n,满足rev(n)=n+d,rev就是n的十进制下翻转(忽略前导零),比如“12340”翻转成“4321”.
d<=1e9
解题思路
化一下方程,rev(n)-n=d。
设ws(x)为x在十进制下的位数,s[i]表示n的第i位的数字,那么我们可以写成
∑i=0..ws(n)−1(s[i]−s[ws−1−i])∗10ws−1−i=d
∑
i
=
0..
w
s
(
n
)
−
1
(
s
[
i
]
−
s
[
w
s
−
1
−
i
]
)
∗
10
w
s
−
1
−
i
=
d
那么合并一下同类项
∑i=0..ws(n)/2−1(s[i]−s[ws−1−i])∗(10ws−1−i−10i)=d
∑
i
=
0..
w
s
(
n
)
/
2
−
1
(
s
[
i
]
−
s
[
w
s
−
1
−
i
]
)
∗
(
10
w
s
−
1
−
i
−
10
i
)
=
d
挖掘一下性质。
ws(n)上界是什么?由于d非0,所以说,ws(n)的上界就是ws(d)*2。
那么我们发现其实ws(n)/2-1非常的小,考虑暴力?
设t[i]=s[i]-s[ws-1-i]。
我们只需要枚举t即可,然后统计在当前方案下有多少个对应的s。
直接搜索肯定TLE,考虑剪枝。
假如确定到t[i],令t[i]=a,无论后面怎么填,如果我的sum总比d小的话,是不可能填t[i]=a-1的。
不得了,每次只剩两个选择,时间复杂度变成
O(22∗log10d)
O
(
2
2
∗
l
o
g
10
d
)
左右。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
const int N=2e6+5,M=1e7+50,mo=1e9+7;
ll ten[20],inc[20],ans,d,upb,i,j,cons,L,D[N],xs;
void dfs(int x,ll y,ll z)
{
if (x>L/2-1)
{
if (L%2) z*=10;
if (y==d)
ans+=z;
return ;
}
ll i=-9;
while (i<=9&&y+i*(ten[L-x-1]-ten[x])<=d) i++;
i--;
D[x]=i;
dfs(x+1,y+i*(ten[L-x-1]-ten[x]),z*(inc[i+cons]-(x==0&&i>=0)));
i++;
if (i>9) return ;
D[x]=i;
dfs(x+1,y+i*(ten[L-x-1]-ten[x]),z*(inc[i+cons]-(x==0&&i>=0)));
//d[i]=s[i]-s[l-1-i],gx=d[i]*(10^i-10^(l-i-1)) ,
//s[L-1]!=0, 当d>=0,存在s[L-1]=0
}
int main()
{
freopen("t19.in","r",stdin);
// freopen("t19.out","w",stdout);
scanf("%lld",&d);
ten[0]=1;
fo(i,1,18) ten[i]=ten[i-1]*10;
cons=10;
fo(i,0,9) fo(j,0,9) inc[i-j+cons]++;
fo(upb,0,9) if (ten[upb]>d) break;
fo(L,0,19)
dfs(0,0,1);
printf("%lld",ans);
}