HDU6562 2018CCPC吉林駅H愛好家(ラインセグメントツリーに良い質問)

質問:
長さn、s1、s2、... snの文字列セットが指定されている場合、各文字列は最初は空の文字列です
。m
操作が指定されると、各操作に2つの操作オプションがあります:
warp lrdこれは、lからrまでの間隔のすべての数値がdsidになることを意味します。たとえば、元のsi =“ 33”、次にd = 5、次にsiが“ 5335”になり
ます。querylrはこの間隔のすべての文字列を尋ねます表現される数値の合計は何ですか、空の文字列は計算するための0と同等です

数値xの場合、xの更新は次と同等です:10 * x + d + d * 10 len(x)、len(x)は現在xの桁数を表す
ため、間隔の合計の場合、更新操作は10 * sum + d *(r-l + 1)+ d * ∑10 len(xi)
なので、最初にラインセグメントツリーを維持する必要があります。ツリー配列は現在のノードの合計を表し、len配列は現在のノードで表される数値の桁の長さを表します。 、そして間隔更新の要件を完了する必要があるため、遅延配列も必要ですが、1つの配列だけを維持するのが容易でない場合は遅延が分離され、lazlは左半分の更新された値を表し、lazrは右半分の更新された値を表します、そして、lazlenは遅延間隔の長さを表します。
クエリ操作は特別なものではありません。
更新操作:

		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;//一侧就只加了一位

最初の合計ツリーでは、左側のvalの位置を残すために、len [rt]に10を掛ける必要があります。
右側のマークは、10とvalを掛けたものです。左側のマークは、マークの長さを掛ける必要があります。元のマークを追加します。片側セクションの長さが記録されるため、最終的なマークの長さは10倍するだけで済みます。

次に、マーク
を押し下げる操作あります。これも最も厄介な操作です。ツリー(つまり、間隔の合計)は2つの部分で取得され、右側にlazlenを加え、親ノードの右側のマークに現在のサブインターバルの長さを掛け、左側に側面は、lazlenを乗算した後、現在の子ノードの桁数を乗算した、親ノードの左マークです。
len配列は、親ノードのlazlen配列と2回乗算する必要があります。lazlenによって維持される左と右のマーク間隔は、2つを合わせた長さが、間隔全体の長さのマーク寄与分だからです。
左のタグ配列は、親ノードの左のタグ配列に現在のラズレンを乗算したもので、元の左のタグ配列が追加されます。
右のタグ配列は、親のノードのラズレンに現在の右のタグ配列と親ノードの右のタグ配列
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
おすすめ