Anica 做了一个很奇怪的梦:她梦见了一个无限大的平板,平板上填着无限行和无限列的整数。有趣的是,每个整数在那神奇的平板上只出现有限的次数。
机智的Anica很快便发现了这其中数字的规律,每一行第一列的数字表示当前的行号,其它非第一列的数字,为该位置左边一列的数字加上其数字的翻转数字之和。为了方便描述,我们定义A[i,j]表示平板上第i行第j列的数字:
1. A[i,1] = i,
2. A[i,j] = A[i,j-1] + Rev( A[i,j-1] ), 其中Rev(x)表示x在10进制下的翻转,例如Rev(213) = 312, Rev(406800) = 008604 = 8604 。
然而,Anica对这堆数字不太感兴趣。她看了一下平板的背面,并发现了一盏神灯,同时神灯也发现了Anica,这时一个精灵冒了出来:
精灵:“Anica,如果你能答对我Q个问题,你将获得一份神秘的礼物,否则你将无法无法醒来,哈哈哈哈~~”
精灵:“每个问题将给你两个整数A和B,请问区间[A,B]的数字总共在平板上出现了多少次?”
Anica:“我不想要你的礼物,我只想要醒过来,我还有很多事情要做呢!”
50%的分数保证1 <= A,B <= 10^6。
100%的分数保证1 <=A,B <=10^10。
还是一道脑洞题。
题目描述中的图非常好,我们就围绕图来讲。
我们可以枚举第一列的数,然后暴力出2,3,4…..n列的数。这样就有50分了(暴力分真多)。
for(i,1,N)
{ ans[i]++; ans[i+rev(i)]+=ans[i]; }
这里,第一列的数灰常多。所以TLE。
我们可以打表,找规律。
发现第二列的数少很多。
于是把第二列的数构造出来,再暴力,就能解决问题了。
问题是如何构造。
我们找一下问题的特点。
模拟一下加法。
a1 a2 a3 a4
+
a4 a3 a2 a1
−−−−−−
a1+a4 a2+a3 a3+a2 a4+a1
令a1+a4=b1,a2+a3=b2.
和就变成:
b1 b2 b2 b1
和的取值范围是0<=bi<=18.
因为A,B<=10^10,所以b最多有5个。
所以复杂度就是19^5。
构造时顺便算一下和的可能性拆分(拆分为2个数的和)。
做一遍前缀和,[L,R]的答案=ans[R]-ans[L-1]。
比较恶心。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
typedef long long ll;
const ll N=ll(1e10);
const int M=2600000;
int m,num,a[6],
b1[19]={1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1};
struct node
{
ll x,y;
}ans[M],b[M];
ll sum[M];
bool cmp(node a,node b){return a.x<b.x;}
ll rev(ll x)
{
ll t=0;
for(;x;x/=10) t=t*10+x%10;
return t;
}
void dfs(int k,int n)
{
if(k>(n+1)/2)
{
int x=(n+1)/2;
ll t=0,y=1,z;
fo(i,1,x)
{
t=t*10+a[i],z=b1[a[i]];
if(n&1 && i==x) z=!(a[i]&1);
else
if(i==1 && a[i]<10) z--;
y*=z;
}
if(n&1) x--;
fd(i,x,1) t=t*10+a[i];
if(t<=N) b[++m].x=t,b[m].y=y;
return;
}
int x=0;
if(k==1) x=1;
fo(i,x,18)
{
if(n&1 && k==n/2+1 && i&1) continue;
a[k]=i;
dfs(k+1,n);
}
a[k]=0;
}
int serch(int l,int r,ll k)
{
while(l<r)
{
int mid=(l+r)>>1;
if(ans[mid].x>k) r=mid;
else l=mid+1;
}
return l-1;
}
int main()
{
freopen("table.in","r",stdin);
freopen("table.out","w",stdout);
fo(i,1,10) dfs(1,i);
sort(b+1,b+m+1,cmp);
fo(i,1,m)
if(b[i].x!=b[i-1].x) ans[++num]=b[i];
else ans[num].y+=b[i].y;
fo(i,1,num)
{
ll j=ans[i].x+rev(ans[i].x);
ans[serch(1,num,j)].y+=ans[i].y;
}
fo(i,1,num) sum[i]=sum[i-1]+ans[i].y;
int _;
ll l,r;
scanf("%d",&_);
while(_--)
{
scanf("%lld %lld",&l,&r);
int posl=serch(1,num,l),posr=serch(1,num,r);
if(ans[posl].x==l) posl--;
printf("%lld\n",sum[posr]-sum[posl]+r-l+1);
}
fclose(stdin);fclose(stdout);
return 0;
}