链接:https://www.nowcoder.com/acm/contest/141/F
来源:牛客网
Eddy likes to play with digits. However, as you may know, Eddy is a programmer not a normal human. Thus, he likes to play with hexadecimal digits(base 16) instead of decimal digits(base 10). One day, he found that sum of digits() is very interesting. Then, he invents following function.
After playing with several times, Eddy found that for one integer, the computation is too easy to make him happy. Thus, Eddy generates a string of hexadecimal digits S, and takes some subsegment(consecutive digits) of it. Then, Eddy takes all the non-empty subsequence(not necessary consecutive digits) from the subsegment as the inputs of the function. It becomes a little challenging for Eddy now. But, Eddy is still not satisfied. He wants to change the string sometimes and keeps taking some subsegments as queries. Now, it's really a problem for Eddy. You, as one of the friends of Eddy, come to rescue him and are going to compute the answer for him.
Since the number of outputs would be too many(which will be equal to the number of non-empty subsequences), you are only required to compute the number of each output and report the number to Eddy.
For example, the hexadecimal string S equals . Eddy takes the subsegment [1,1] which is . All the non-empty subsequence is . Thus, the answer will be .
If Eddy takes the subsegment [1,3] which is . All the non-empty subsequence is . Then, the answer will be 267411465.
First line of input contains two space-separated integer N, Q indicating the length of hexadecimal digits S and number of operations Eddy will take. Second line of input contains a string S indicating the hexadecimal string Eddy generates. Following Q lines, each line will be one of following form: : changing p-th digit of S into c. : taking the subsegment [l, r] and compute the answer. 1≤ N,Q≤ 105 |S|=N, length of S will be N character of S will be hexadecimal digit() 1≤ p ≤ N, c will be hexadecimal digit 1≤ l ≤ r≤ N
For each second type operation(), output one line indicating the corresponding answer.
示例1
复制
5 2 12345 2 1 1 2 1 3
复制
1021 267411465
示例2
复制
5 3 12345 2 1 5 1 1 A 2 1 5
复制
930616025 659780022
题目大意:题目定义了一个函数SOD(v),如果v为以内的数就直接返回v,否则就返回各位上数的数位之和(十六进制)的SOD。
对于每个集合,定义ans值为sum{集合内数字 i 的个数 *(1021)^ i }
初始给出一个位数为n的十六进制数S,接下来有q次操作,每次操作有以下两种可能
1 p c :将S的第 p 位的数换成为c
2 l r :查询S的第 l 位到第 r 位的数字任选x个数(x<=r-l+1)的和的SOD值所组成的集合的ans
题目思路:由于查询区间内的值存在可以任选的情况,我们就可以借助线段树的区间合并来处理这个问题。线段树的每一个结点用一个数组来记录每个SOD值出现的次数,每次从子节点向父节点更新的时候就把两个子节点的SOD值合并更新至父节点,由于可以任取数字,所以得将两个子树内的num值的所有情况进行枚举,再相乘。查询的时候再查询出对应的区间即可。注意一个地方就是,在十六进制下不能直接取模,因为会有进位的原因,所以得用-15的方式来取模。
具体实现看代码:
#include
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lowbit(x) x&-x
#define MP make_pair
#define pb push_back
#define debug(x) cout<<"x= "< vi;
typedef pairpii;
const int mod=1e9+7;
const ll infll=0x3f3f3f3f3f3f3f3f;
const int MX = 1e5 + 7;
const double EPS=1e-8;
const int INF = 0x3f3f3f3f;
int n,m;
ll f[16];
char str[MX];
int getc(char ch){
if(ch>='0' && ch<='9') return ch-'0';
return ch-'A'+10;
}
struct Tree{
ll num[16];
void init(){
memset(num,0,sizeof(num));
}
Tree operator+(const Tree&A)const{
Tree res;res.init();
for(int i=0;i<16;i++){
res.num[i]=(res.num[i]+A.num[i]+num[i])%mod;
for(int j=0;j<16;j++){
int cnt=i+j;if(cnt>15) cnt-=15;
res.num[cnt]=(res.num[cnt]+A.num[i]*num[j]%mod)%mod;
}
}
return res;
}
}tree[MX<<2];
void push_up(int rt){
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void build(int l,int r,int rt){
tree[rt].init();
if(l==r){
int x=getc(str[l]);
tree[rt].num[x]=1;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
push_up(rt);
}
void update(int p,int d,int l,int r,int rt){
if(l==r){
tree[rt].init();
tree[rt].num[d]=1;
return;
}
int m=(l+r)>>1;
if(p<=m) update(p,d,lson);
else update(p,d,rson);
push_up(rt);
}
Tree query(int L,int R,int l,int r,int rt){
if(L<=l && r<=R) return tree[rt];
int m=(l+r)>>1;
if(L<=m && R>m) return query(L,R,lson)+query(L,R,rson);
if(L<=m) return query(L,R,lson);
return query(L,R,rson);
}
ll cal(Tree A){
ll res=0;
for(int i=0;i<16;i++){
//cout<