【GDOI模拟】数字方阵

Description

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 。【GDOI模拟】数字方阵_第1张图片
然而,Anica对这堆数字不太感兴趣。她看了一下平板的背面,并发现了一盏神灯,同时神灯也发现了Anica,这时一个精灵冒了出来:
精灵:“Anica,如果你能答对我Q个问题,你将获得一份神秘的礼物,否则你将无法无法醒来,哈哈哈哈~~”
精灵:“每个问题将给你两个整数A和B,请问区间[A,B]的数字总共在平板上出现了多少次?”
Anica:“我不想要你的礼物,我只想要醒过来,我还有很多事情要做呢!”

Solution

范围那么大,不是直接找规律算就是预处理好。
规律找不出来,我们选择预处理。
首先我们可以知道一个东西,所有表格中 1010 内的数字一定在第二列出现过,那么我们可以先求出第二列所有出现过的数并把个数计算出来排一个序,然后询问时二分查找一下区间。所有出现在这个序列中的数出现个数都是不为1的,其他没有出现过得数出现个数都是1。很显然。
问题是我们要怎么得到这个序列。
a1 a2 a3 a4 a5 a6
+a6 a5 a4 a3 a2 a1
————————————————————————————————————
a1+a6 a2+a5 a3+a4 a3+a4 a2+a5 a1+a6
我们可以知道第二列的数都是由一个数对称过来的,所以只要枚举前面的5个数字就可以了。我们可以知道a1,a2….an都是[0…9]的数字,所以对称后每一位最多为18,所以得到的序列中最多只有 185 个数字。那么个数怎么求呢?因为每一位数是由两个数相加而来的,所以会有多种方案,那么总方案用乘法原理乘起来就好了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=6400007;
typedef long long ll;
ll i,j,k,l,ans,r,yi,er;
ll n,m,shu[20],t;
ll aa,bb,cc,dd,ee;
ll add[maxn],data[6],first,end,num,has[maxn+5],h[maxn+5];
struct node{
    ll x,y;
}a[maxn];
bool cmp(node x,node y){return x.y<y.y;}
ll rev(ll x){
    ll y=0,i,j,k;
    while(x){
        k=x%10;
        y=y*10+k;
        x/=10;
    }
    return y;
}
ll hash(ll x) {
    ll t=x%maxn;
    while (h[t]&&h[t]!=x) t=t%maxn+1;
    return t;
}
void dfs(ll x){
    ll i,j=0,k=0,sum=0;
    if(x>n){
        ll u=1;
        fo(i,1,n){
            if(i==n&&m%2){
                if(data[i]%2==0)k=1;else k=0;
            }
            else k=shu[data[i]];
            if(i==1&&(i!=n||m%2==0)&&data[i]<10)k--;
            u*=k;
        }
        if(!u)return;
        fod(i,m,1){
            if(i>n)j=data[m-i+1];
            else j=data[i];
            sum=sum*10+j;
        }
        k=hash(sum);
        if(h[k]!=sum){
            a[++num].x=u;
            a[num].y=sum;
            h[k]=sum;
            has[k]=num;
        }
        else{
            a[has[k]].x+=u;
        }
        return;
    }
    else{
        j=0;
        if(x==1)j=1;
        fo(i,j,18){
            data[x]=i;
            dfs(x+1);
        }
    }

}
ll find(ll x,ll y,ll z){
    ll mid;
    while(x<y){
        mid=(x+y)/2;
        if(a[mid].y>z)y=mid;else x=mid+1;
    }
    return x-1;
}
int main(){
    freopen("table.in","r",stdin);
    freopen("table.out","w",stdout);
    scanf("%d",&t);
    fo(i,0,9)fo(j,0,9)shu[i+j]++;
    fo(i,1,10){
        n=i/2+i%2;
        m=i;
        dfs(1);
    }
    sort(a+1,a+1+num,cmp);
    fo(i,1,num){ 
        k=a[i].y+rev(a[i].y);
        if(k<=a[num].y)a[find(1,num,k)].x+=a[i].x;
    }
    fo(i,1,num)add[i]+=add[i-1]+a[i].x;
    while(t--){
        scanf("%lld%lld",&l,&r);
        int u=find(1,num,l),v=find(1,num,r);
        if(a[u].y==l)u--;
        printf("%lld\n",add[v]-add[u]+r-l+1);

    }
}

你可能感兴趣的:(数论,预处理,二分,GDOI,数字方阵)