题意:
对于一个(B)进制数(x)定义它的数根为把所有位上的数加在一起的结果。
给出一个长度为(n)的(B)进制串(s),(m)次询问每次给定一个集合(A)和一个数(x),计算有多少个(s[l ... r])可以通过将至多一个字符更改为(A)中的元素使得数根为(x)。
(n,mleq 2^{20},B<=16),时间限制8s。
首先,考虑快速求一个数的数根(f(x))。
设(B)进制数(x=sum_{i=0}^{n}a_iB^i),(y=sum_{i=0}^{n}a_i)。那么(y-x=sum_{i=0}^{n}a_i(B^i-1))。由等比数列求和公式,(B-1|B^i-1)。因此(xequiv y mod B-1),即(xequiv f(x) mod B-1)。
那么维护总和模(B-1)为(x)的区间数量,就可以处理不进行修改的情况。
对于修改操作,假设一个区间原来的总和模(B-1)为(y),那么只要把其中的一个数字增加(t=x-y),就能满足条件。
设给出的集合为(A),再设(C={x|x+tin A}),那么只要这个总和为(y)的区间内元素的集合与(C)的交集不为空,这个区间就满足条件。
我们只要对于所有(0leq y<B-1)和所有集合(S)求出总和为(y),区间内元素集合为(S)的区间数目,对于交集不为空这个条件用(FMT)预处理一下就行。
对于这个子问题,我们可以枚举右端点(r),并对于每个(x)维护从(r)往左第一个等于(x)的位置。这些位置把序列分为了若干段,每一段的(S)都是相等的。
对于每一段分别求即可。
这个使用前缀和即可优化至(O(nB^2))。
现在我们已经解决了大部分情况,但是还有几个细节:
首先,对于(x=0)的询问,要求区间内都是0。这个我们可以维护全是(0)的以及只有一个非零位置的区间个数来计算。
对于(x=B-1)的询问,需要考虑把所有数字都改为0的情况。
若(B-1 otin A),那么全是0的需要减去。同时若(0in A),那么长度为1的元素不为0的区间应当减去。
并且对于只有一个非零(x)的长度至少为2的区间,若(B-1-x otin A),那么这个也得减去。
总时间复杂度为(O((n+m)*B^2))。能过。
参考代码:
#include <stdio.h>
#include <string.h>
#define ll long long
char zf[1048580];int ch[300],su[1048580];
int sz[1048580],la[16],he[16][1048580];
void swap(int &a,int &b)
{
int t=a;a=b;b=t;
}
void fmt(ll sz[32768],int B)
{
for(int j=0;j<B;j++)
{
for(int i=0;i<(1<<B);i++)
{
if(i&(1<<j))
sz[i]+=sz[i^(1<<j)];
}
}
}
ll ss[16][32768],s1[16];int cs[16];
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
int n,m,B;ll s0=0;
scanf("%d%d%d%s",&n,&m,&B,zf);
for(int i=0;i<10;i++)
ch[i+'0']=i;
for(int i=0;i<6;i++)
ch[i+'a']=i+10;
he[0][0]=1;
for(int i=1;i<=n;i++)
{
sz[i]=ch[zf[i-1]];
cs[sz[i]]+=1;
su[i]=(su[i-1]+sz[i])%(B-1);
for(int j=0;j<B-1;j++)
he[j][i]=he[j][i-1];
he[su[i]][i]+=1;
}
for(int r=1,l1=0,l2=0;r<=n;r++)
{
if(sz[r]){l1=l2;l2=r;}
s0+=r-l2;
if(l2)s1[sz[l2]]+=l2-l1-(l2==r);
la[sz[r]%(B-1)]=r;int px[17],pz[17];
for(int i=0;i<B-1;i++)
px[i]=la[i],pz[i]=i;
px[B-1]=0;
for(int i=0;i<B-1;i++)
{
for(int j=i+1;j<B-1;j++)
{
if(px[i]<px[j])
swap(px[i],px[j]),swap(pz[i],pz[j]);
}
}
for(int i=0,z=0;i<B-1&&px[i];i++)
{
z|=(1<<pz[i]);
for(int j=0;j<B-1;j++)
{
int t=(su[r]-j+B-1)%(B-1);
ss[j][z]+=he[t][px[i]-1];
if(px[i+1])ss[j][z]-=he[t][px[i+1]-1];
}
}
}
for(int i=0;i<B-1;i++)fmt(ss[i],B-1);
for(int i=0;i<m;i++)
{
char xx[2],zz[20];bool bk[16]={0};
scanf("%s%s",xx,zz);
int x=ch[xx[0]],s=strlen(zz);
for(int j=0;j<s;j++)bk[ch[zz[j]]]=true;
if(x==0)
{
ll ans=s0;
if(bk[0])
{
for(int j=1;j<B;j++)
ans+=cs[j]+s1[j];
}
printf("%lld
",ans);
continue;
}
ll ans=ss[x%(B-1)][(1<<(B-1))-1];
for(int y=0;y<B-1;y++)
{
if(x%(B-1)==y)continue;
int t=(x-y+B-1)%(B-1),z=0;
for(int j=0;j<s;j++)
z|=(1<<((ch[zz[j]]-t+B-1)%(B-1)));
ans+=ss[y][(1<<(B-1))-1];
ans-=ss[y][((1<<(B-1))-1)^z];
}
if(x==B-1&&!bk[B-1])
{
ans-=s0;
if(bk[0])
{
for(int j=1;j<B-1;j++)
{
ans-=cs[j];
if(!bk[B-1-j])ans-=s1[j];
}
}
}
printf("%lld
",ans);
}
return 0;
}