题意:
给定一个长度为n的字符串集,s1,s2,…sn,每个字符串一开始都是空串
现在给给定m次操作
每次操作都有两种操作选择:
warp l r d 代表把l到r这个区间内的每一个数变成dsid,例如原本si=“33”,然后d = 5,那么si就变为了“5335”.
query l r 询问这个区间内的每个字符串所代表的的数字的和为多少,空串相当于0来计算
对于一个数x更新x相当于:10 * x + d + d * 10len(x),len(x)代表x这个数当前有几位
这样对于一个区间sum来说更新操作就相当于:10 * sum + d * (r-l+1) + d * ∑10len(xi).
所以我们需要先维护线段树一个tree数组代表当前节点的和还有一个len数组代表当前节点代表的数的数位长度,然后因为需要完成区间更新的要求,所以还需要lazy数组,但是如果只用一个数组不好维护,所以把lazy分开,一个lazl代表左半部分的更新值,一个lazr代表右半部分的更新值,然后一个lazlen代表lazy区间的长度.
查询操作没什么特别的。
更新操作的话:
tree[rt] = (tree[rt]*10%MOD + val*(r-l+1)%MOD + 10*val*len[rt]%MOD)%MOD;
len[rt] = len[rt]*100%MOD;
lazr[rt] = (lazr[rt]*10%MOD + val)%MOD;
lazl[rt] = (val*lazlen[rt]%MOD + lazl[rt])%MOD;
lazlen[rt] = lazlen[rt]*10%MOD;//一侧就只加了一位
第一个求和tree时,len[rt]还要再乘上个10,为左边的val空出位置
右标记乘以10加上val即可,左侧标记需要先val乘以标记的长度再加上原先的做标记即可,最后标记长度只需要乘以10即可,因为记录的是一侧区间的长度.
然后就是下推标记的操作,也是最麻烦的操作:
tree(也就是区间sum)分两部分得到,右边就是乘以lazlen加上父节点的右标记乘以当前的子区间的长度即可,左侧就是父节点的左标记乘以lazlen再乘以当前子节点的数位长度。
而len数组需要乘以两次父节点的lazlen数组,因为lazlen维护的左右标记区间,两个的长度合在一起才是总的区间长度的标记贡献。
左标记数组,就是父节点的左标记数组乘以当前的lazlen在加上原先的左标记数组
右标记数组,就是父节点的lazlen乘以当前的右标记数组再加上父节点的右标记数组
lazlen就直接乘父节点我的lazlen即可
别忘了全部做完这些操作之后,把所有的标记清空掉
void pushdown(int rt,int tot)
{
if(lazlen[rt] > 1){
//子节点 第一部分 当前子节点乘以父节点lazy的长度 再加上父节点的有lazy即为子节点的右边答案
//第二部分 父节点 的 做lazy值乘以父节点的lazy长度乘以子节点的长度 这两部分加起来 即是子节点更新完成的答案
tree[rt<<1] = (tree[rt<<1]*lazlen[rt]%MOD + (tot-tot/2)*lazr[rt]%MOD + (lazl[rt]*lazlen[rt])%MOD*len[rt<<1]%MOD)%MOD;
tree[rt<<1|1] = (tree[rt<<1|1]*lazlen[rt]%MOD + (tot/2)*lazr[rt]%MOD + (lazl[rt]*lazlen[rt])%MOD*len[rt<<1|1]%MOD)%MOD;
len[rt<<1] = len[rt<<1]*lazlen[rt]%MOD*lazlen[rt]%MOD;//乘两次 才能把他们的贡献 都算上
len[rt<<1|1] = len[rt<<1|1]*lazlen[rt]%MOD*lazlen[rt]%MOD;
lazl[rt<<1] = (lazlen[rt<<1]*lazl[rt]%MOD + lazl[rt<<1])%MOD;
lazl[rt<<1|1] = (lazlen[rt<<1|1]*lazl[rt]%MOD + lazl[rt<<1|1])%MOD;
lazr[rt<<1] = (lazlen[rt]*lazr[rt<<1]%MOD + lazr[rt])%MOD;//空出 父节点 lazy个长度 来让右边的lazy更新上
lazr[rt<<1|1] = (lazlen[rt]*lazr[rt<<1|1]%MOD + lazr[rt])%MOD;
lazlen[rt<<1] = (lazlen[rt<<1]*lazlen[rt])%MOD;//只是一边的长度 不需要 两边 都乘以这个长度
lazlen[rt<<1|1] = (lazlen[rt<<1|1]*lazlen[rt])%MOD;
lazr[rt] = lazl[rt] = 0;
lazlen[rt] = 1;//别忘了消除标记的影响
}
}
剩下的都是常规操作了
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+7;
const int MOD = 1e9+7;
char s[17];
ll tree[MAXN<<2],len[MAXN<<2],lazl[MAXN<<2],lazr[MAXN<<2],lazlen[MAXN<<2];
void build(int rt,int l,int r)
{
tree[rt] = lazr[rt] = lazl[rt] = 0;
lazlen[rt] = 1;
if(l == r){
len[rt] = 1;
return ;
}
int mid = (l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
len[rt] = (len[rt<<1] + len[rt<<1|1])%MOD;
}
void pushdown(int rt,int tot)
{
if(lazlen[rt] > 1){
//子节点 第一部分 当前子节点乘以父节点lazy的长度 再加上父节点的有lazy即为子节点的右边答案
//第二部分 父节点 的 做lazy值乘以父节点的lazy长度乘以子节点的长度 这两部分加起来 即是子节点更新完成的答案
tree[rt<<1] = (tree[rt<<1]*lazlen[rt]%MOD + (tot-tot/2)*lazr[rt]%MOD + (lazl[rt]*lazlen[rt])%MOD*len[rt<<1]%MOD)%MOD;
tree[rt<<1|1] = (tree[rt<<1|1]*lazlen[rt]%MOD + (tot/2)*lazr[rt]%MOD + (lazl[rt]*lazlen[rt])%MOD*len[rt<<1|1]%MOD)%MOD;
len[rt<<1] = len[rt<<1]*lazlen[rt]%MOD*lazlen[rt]%MOD;//乘两次 才能把他们的贡献 都算上
len[rt<<1|1] = len[rt<<1|1]*lazlen[rt]%MOD*lazlen[rt]%MOD;
lazl[rt<<1] = (lazlen[rt<<1]*lazl[rt]%MOD + lazl[rt<<1])%MOD;
lazl[rt<<1|1] = (lazlen[rt<<1|1]*lazl[rt]%MOD + lazl[rt<<1|1])%MOD;
lazr[rt<<1] = (lazlen[rt]*lazr[rt<<1]%MOD + lazr[rt])%MOD;//空出 父节点 lazy个长度 来让右边的lazy更新上
lazr[rt<<1|1] = (lazlen[rt]*lazr[rt<<1|1]%MOD + lazr[rt])%MOD;
lazlen[rt<<1] = (lazlen[rt<<1]*lazlen[rt])%MOD;//只是一边的长度 不需要 两边 都乘以这个长度
lazlen[rt<<1|1] = (lazlen[rt<<1|1]*lazlen[rt])%MOD;
lazr[rt] = lazl[rt] = 0;
lazlen[rt] = 1;//别忘了消除标记的影响
}
}
void update(int rt,int l,int r,int L,int R,int val)
{
if(l >= L && r <= R){
tree[rt] = (tree[rt]*10%MOD + val*(r-l+1)%MOD + 10*val*len[rt]%MOD)%MOD;
len[rt] = len[rt]*100%MOD;
lazr[rt] = (lazr[rt]*10%MOD + val)%MOD;
lazl[rt] = (val*lazlen[rt]%MOD + lazl[rt])%MOD;
lazlen[rt] = lazlen[rt]*10%MOD;//一侧就只加了一位
return ;
}
pushdown(rt,r-l+1);
int mid = (l+r)>>1;
if(L <= mid) update(rt<<1,l,mid,L,R,val);
if(R > mid) update(rt<<1|1,mid+1,r,L,R,val);
tree[rt] = (tree[rt<<1] + tree[rt<<1|1])%MOD;
len[rt] = (len[rt<<1] + len[rt<<1|1])%MOD;
}
ll query(int rt,int l,int r,int ql,int qr)
{
if(l >= ql && r <= qr){
return tree[rt];
}
pushdown(rt,r-l+1);
int mid = (l+r)>>1;
ll ans = 0;
if(ql <= mid) ans = (ans+ query(rt<<1,l,mid,ql,qr))%MOD;
if(qr > mid) ans = (ans + query(rt<<1|1,mid+1,r,ql,qr))%MOD;
return ans;
}
int main()
{
int T,cas = 0;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
build(1,1,n);
printf("Case %d:\n",++cas);
while(m--){
int l,r,x;
scanf("%s",s);
if(s[0] == 'w'){
scanf("%d%d%d",&l,&r,&x);
update(1,1,n,l,r,x);
}
else if(s[0] == 'q'){
scanf("%d%d",&l,&r);
ll ans = query(1,1,n,l,r);
printf("%lld\n",ans);
}
}
}
return 0;
}