Hdu4348_To the moon(主席树)

题意:

一个长度为n的数组,4种操作 :

(1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 。

(2)Q l r:查询当前时间戳区间[l,r]中所有数的和 。

(3)H l r t:查询时间戳t区间[l,r]的和 。

(4)B t:将当前时间戳置为t 。

思路:

对于每一次区间加法都新建节点建一棵线段树,加法不用向下更新,懒惰标记最后加上就行了,查询的话就是某个根节点的线段树求和。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 100000+5;
int n, m;

int lson[maxn<<5], rson[maxn<<5], root[maxn<<1];
LL Tree[maxn<<5], lazy[maxn<<5];

int cnt;
void Build(int& rt, int l, int r){
	rt = ++cnt; lazy[rt] = 0;
	if(l == r){
		scanf("%lld", &Tree[rt]);
		return;
	}
	int mid = (l+r) >> 1;
	Build(lson[rt], l, mid);
	Build(rson[rt], mid+1, r);
	Tree[rt] = Tree[lson[rt]] + Tree[rson[rt]];
}

void update(int& new_rt, int old_rt, int la, int rb, int l, int r, int val){
	new_rt = ++cnt;
	lson[new_rt] = lson[old_rt]; rson[new_rt] = rson[old_rt]; 
	lazy[new_rt] = lazy[old_rt]; Tree[new_rt] = Tree[old_rt];
	
//	Tree[new_rt]+= (LL)val*(rb-la+1);
	
	if(la <= l&&rb >= r){
		lazy[new_rt]+= val;
		Tree[new_rt]+= (LL)val*(r-l+1);
		return;
	}
	int mid = (l+r) >> 1;
//	if(rb <= mid) return update(lson[new_rt], lson[old_rt], la, rb, l, mid, val);
//    else if(la > mid) update(rson[new_rt], rson[old_rt], la, rb, mid+1, r, val);
//    else{
//        update(lson[new_rt], lson[old_rt], la, mid, l, mid, val);
//        update(rson[new_rt], rson[old_rt], mid+1, rb, mid+1, r, val);
//    }
// or:
	if(la <= mid) update(lson[new_rt], lson[old_rt], la, rb, l, mid, val);
	if(rb > mid)  update(rson[new_rt], rson[old_rt], la, rb, mid+1, r, val);
	Tree[new_rt] = Tree[lson[new_rt]] + Tree[rson[new_rt]] + lazy[new_rt]*(r-l+1);
}

// 查询根为rt的线段树区间和 
LL Query(int la, int rb, int l, int r, int rt){
	if(la > r||rb < l) return 0;
	if(la <= l&&rb >= r) return Tree[rt];
	int mid = (l+r) >> 1;
	LL ans = (LL)lazy[rt]*(rb-la+1);
//  错误做法: 
//	if(la <= mid) ans+= Query(la, rb, l, mid, lson[rt]);
//	if(rb > mid)  ans+= Query(la, rb, mid+1, r, rson[rt]);
//	return ans;
	if(rb <= mid) return ans + Query(la, rb, l, mid, lson[rt]);
	else if(la > mid) return ans + Query(la, rb, mid+1, r, rson[rt]);
	else return ans + Query(la, mid, l, mid, lson[rt]) + Query(mid+1, rb, mid+1, r, rson[rt]);
}

int main()
{
	char ch[5];
	int l,r,t,d, now;
	bool flag = false;
	while(scanf("%d%d",&n,&m) == 2){
		if(flag) printf("\n");
		flag = true;
		cnt = 0;
		Build(root[0], 1, n);//for(int i = 0; i <= 40; ++i) printf("%d ",Tree[i]);puts("");
		now = 0;	// 当前的根 
		
		for(int i = 0; i < m; ++i){
			scanf("%s", ch);
			if(ch[0] == 'Q'){
				scanf("%d%d", &l,&r);
				printf("%lld\n", Query(l, r, 1, n, root[now]));
			}
			else if(ch[0] == 'C'){
				scanf("%d%d%d", &l,&r,&d);
				++now;
				update(root[now], root[now-1], l, r, 1, n, d);
				//for(int i = 0; i <= 40; ++i) printf("%d ",Tree[i]);puts("");
			}
			else if(ch[0] == 'H'){
				scanf("%d%d%d", &l,&r,&t);
				printf("%lld\n", Query(l, r, 1, n, root[t]));
			}
			else if(ch[0] == 'B'){
				scanf("%d", &now);
				
				cnt = root[now+1];	//垃圾回收,返回到上一个版本后,中间的节点可以再次被利用 
			}
		}
	}
	fclose(stdin);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值