Description
对于一个十进制数字 v v ,定义:若 v<16 v < 16 则 SOD(v)=v S O D ( v ) = v ,否则 SOD(v)=SOD(S(v)) S O D ( v ) = S O D ( S ( v ) ) ,其中 S(v) S ( v ) 为将 v v 的十六进制每位数的和。现在给出一个由进制数组成的长度为 n n 的字符串,有 q q 次操作,操作分两种:
将 s s 的第位改成 c c
对区间 [l,r] [ l , r ] 的所有非空子序列求 SOD S O D 值
Input
第一行两个整数 n,q n , q ,之后输入一个由十六进制数组成的长度为 n n 的字符串,最后行每行一个操作
(1≤n,q≤105) ( 1 ≤ n , q ≤ 10 5 )
Output
对于每次查询操作,假设 SOD S O D 值为 i i 的子序列有个,那么输出 ∑i=015ai⋅1021i mod 109+7 ∑ i = 0 15 a i ⋅ 1021 i m o d 10 9 + 7
Sample Input
5 2
12345
2 1 1
2 1 3
Sample Output
1021
267411465
Solution
对于十六进制数
v=∑vi16i
v
=
∑
v
i
16
i
,在求
SOD(v)
S
O
D
(
v
)
时每递归一次
v
v
会减少,而该减少值显然是
15
15
的倍数,直至
v
v
不超过递归终止,那么有
而注意到 SOD(x⋅16i+y)=SOD(x+y) S O D ( x ⋅ 16 i + y ) = S O D ( x + y ) ,故可以用线段树维护每个区间中 0,...,15 0 , . . . , 15 出现的次数,区间合并即为两个长度为 16 16 序列的卷积,时间复杂度 O(162nlogn) O ( 16 2 n l o g n )
Code
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
#define maxn 100005
#define mod 1000000007
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/mod*mod;
}
int add(int x,int y)
{
x+=y;
if(x>=mod)x-=mod;
return x;
}
int SOD(int x)
{
if(x==0)return 0;
if(x%15==0)return 15;
return x%15;
}
int deal(char c)
{
if(c>='0'&&c<='9')return c-'0';
return 10+c-'A';
}
int n,q,f[16];
struct node
{
int x[16];
void init()
{
memset(x,0,sizeof(x));
}
node operator+(const node&b)const
{
node c;
c.init();
for(int i=0;i<16;i++)
for(int j=0;j<16;j++)
c.x[SOD(i+j)]=add(c.x[SOD(i+j)],mul(x[i],b.x[j]));
return c;
}
}Num[maxn<<2],ans;
char s[maxn];
#define ls (t<<1)
#define rs ((t<<1)|1)
void push_up(int t)
{
Num[t]=Num[ls]+Num[rs];
}
void build(int l,int r,int t)
{
Num[t].init();
if(l==r)
{
Num[t].x[0]=add(Num[t].x[0],1);
Num[t].x[deal(s[l])]=add(Num[t].x[deal(s[l])],1);
return ;
}
int mid=(l+r)/2;
build(l,mid,ls),build(mid+1,r,rs);
push_up(t);
}
void update(int x,int l,int r,int t,char c)
{
if(l==r)
{
Num[t].x[deal(s[l])]=add(Num[t].x[deal(s[l])],mod-1);
Num[t].x[deal(c)]=add(Num[t].x[deal(c)],1);
return ;
}
int mid=(l+r)/2;
if(x<=mid)update(x,l,mid,ls,c);
else update(x,mid+1,r,rs,c);
push_up(t);
}
node query(int L,int R,int l,int r,int t)
{
if(L==l&&r==R)return Num[t];
int mid=(l+r)/2;
if(R<=mid)return query(L,R,l,mid,ls);
if(L>mid)return query(L,R,mid+1,r,rs);
return query(L,mid,l,mid,ls)+query(mid+1,R,mid+1,r,rs);
}
int main()
{
f[0]=1;
for(int i=1;i<16;i++)f[i]=mul(1021,f[i-1]);
scanf("%d%d%s",&n,&q,s+1);
build(1,n,1);
while(q--)
{
int op,l,r;
char c[3];
scanf("%d",&op);
if(op==1)
{
scanf("%d%s",&l,c);
update(l,1,n,1,c[0]);
s[l]=c[0];
}
else
{
scanf("%d%d",&l,&r);
ans=query(l,r,1,n,1);
ans.x[0]=add(ans.x[0],mod-1);
int res=0;
for(int i=0;i<16;i++)res=add(res,mul(f[i],ans.x[i]));
printf("%d\n",res);
}
}
return 0;
}