HDU6562 2018CCPC吉林站 H lovers (线段树好题)

题意:
给定一个长度为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;
}

猜你喜欢

转载自blog.csdn.net/weixin_45672411/article/details/108623635