您的位置:首页 > 其它

【GDOI2016模拟4.23】数字方阵

2016-04-26 19:34 316 查看

Description

有一个无限大的矩阵A,满足A[i,1]=i,A[i,j]=A[i,j-1]+rev(a[i,j-1])

求这个矩阵中,在区间a~b中间的数的数量。

多组询问。

Type<=10^5,a<=b<=10^10

Solution

一眼数论(蒟蒻)

然后发现不会打。

其实我们可以发现,对于每一行,都是递增存在的,并且出现2次以上的数不会特别多,也就(19^5+19^4+19^3+19^2+19^1)*2个那么多。

那么,我们可以预处理出所有在第二列出现过的数,以及他们的出现次数。

a1a2a3…aL+aLaL-1…a1=x1….xL

那么对称过来每一位的取值相乘就是次数。

注意a1不能为0。

打完这个表,我们可以再处理处每个数的实际出现次数。

从小到大拍一遍序,然后每一位sum[i+rev(i)]+=sum[i]

这样每一个询问二分求解就行了。

细节比较多,自己想(看代码)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 6400005
using namespace std;
typedef long long ll;
struct note{ll v,sum;}a
;
bool cmp(note x,note y) {return x.v<y.v;}
int ty,len,tot,k,app[20];
ll b[6],sum
,h[N+5],ha[N+5],x,y;
ll hash(ll x) {
int t=x%N;
while (h[t]&&h[t]!=x) t=t%N+1;
return t;
}
void dfs(int x) {
if (x>k) {
ll v=0,cnt,l,x,sum=1;
fo(i,1,k) {
if (i==k&&len%2) x=!(b[i]%2);
else x=app[b[i]];
if (i==1&&!(i==k&&len%2)&&b[i]<10) x--;
sum*=x;
}
if (!sum) return;
fd(i,len,1) {
if (len%2) l=2*k-i;else l=2*k-i+1;
if (i>k) cnt=b[l];
else cnt=b[i];
v=v*10+cnt;
}
l=hash(v);
if (!h[l]) a[++tot].v=v,a[tot].sum=sum,h[l]=v,ha[l]=tot;
else a[ha[l]].sum+=sum;
return;
}
int low;
if (x==1) low=1;else low=0;
fo(i,low,18) b[x]=i,dfs(x+1);
}
ll rev(ll x) {
ll z=0;
while (x) {z=z*10+x%10;x/=10;}
return z;
}
int find(int l,int r,ll x) {
int mid;
while (l<r) {
mid=(l+r)/2;
if (a[mid].v>x) r=mid;else l=mid+1;
}
return l-1;
}
int main() {
freopen("table.in","r",stdin);
freopen("table.out","w",stdout);
fo(i,0,9) fo(j,0,9) app[i+j]++;
for(len=1;len<=10;len++) k=len/2+len%2,dfs(1);
sort(a+1,a+tot+1,cmp);
fo(i,1,tot) {
ll k=a[i].v+rev(a[i].v);
if (k<=a[tot].v) a[find(1,tot,k)].sum+=a[i].sum;
}
fo(i,1,tot) sum[i]=sum[i-1]+a[i].sum;
for(scanf("%d",&ty);ty;ty--) {
scanf("%lld%lld",&x,&y);
int l=find(1,tot,x),r=find(1,tot,y);
if (a[l].v==x) l--;
printf("%lld\n",sum[r]-sum[l]+y-x+1);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: