HDU - 4348 To the moon(可持久化线段树)

题目链接:To the moon - HDU 4348 - Virtual Judge (ppsucxtt.cn)

题意:

给你一串序列,每次对他有以下四种操作:

1.C l r d:将l到r之间的所有数+d,同时时间+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>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=4e6+10,M=1e5+10;
int a[M],ln[N],rn[N],lazy[N],idx;
int root[M];
ll sum[N];
//l和r为当前区间左右端点 
void pushup(int id,int l,int r)
{
	sum[id]=sum[ln[id]]+sum[rn[id]]+(r-l+1)*lazy[id];
}
void build(int id,int L,int R)
{
	lazy[id]=0;sum[id]=0;
	if(L==R)
	{
		sum[id]=a[L];
		return ;
	}
	int mid=L+R>>1;
	ln[id]=++idx;rn[id]=++idx;
	build(ln[id],L,mid);
	build(rn[id],mid+1,R);
	pushup(id,L,R);
}
//l和r为当前区间左右端点,L和R为目标区间左右端点
void update_interval(int pre,int id,int L,int R,int val,int l,int r)
{
	ln[id]=ln[pre];rn[id]=rn[pre],sum[id]=sum[pre],lazy[id]=lazy[pre];
	if(l>=L&&r<=R)
	{
		lazy[id]+=val;
		sum[id]+=(r-l+1)*val;
		return ;
	}
	int mid=l+r>>1;
	if(L<=mid) ln[id]=++idx,update_interval(ln[pre],ln[id],L,R,val,l,mid);
	if(mid+1<=R) rn[id]=++idx,update_interval(rn[pre],rn[id],L,R,val,mid+1,r);
	pushup(id,l,r);
}
//l和r为当前区间左右端点,L和R为目标区间左右端点
ll query_interval(int id,int L,int R,ll lz,int l,int r)
{
	if(l>=L&&r<=R) return lz*(r-l+1)+sum[id];
	int mid=l+r>>1;
	ll ans=0;
	if(L<=mid) ans+=query_interval(ln[id],L,R,lz+lazy[id],l,mid);
	if(mid+1<=R) ans+=query_interval(rn[id],L,R,lz+lazy[id],mid+1,r);
	return ans;
}
int main()
{
	int n,m;
	char op[5];
	while(cin>>n>>m)
	{
		idx=0;
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		root[0]=++idx;
		build(root[0],1,n);
		int ll,rr,now=0,val,t;
		for(int i=1;i<=m;i++)
		{
			scanf("%s",op);
			if(op[0]=='Q')
			{
				scanf("%d%d",&ll,&rr);
                //查询当前时间下的l到r的值的和
				printf("%lld\n",query_interval(root[now],ll,rr,0,1,n));
			}
			else if(op[0]=='C')
			{
				scanf("%d%d%d",&ll,&rr,&val);
				root[++now]=++idx;
                //对区间l到r上的值进行修改
				update_interval(root[now-1],root[now],ll,rr,val,1,n);
			}
			else if(op[0]=='H')
			{
				scanf("%d%d%d",&ll,&rr,&t);
                //查询时间t下的l到r的值的和
				printf("%lld\n",query_interval(root[t],ll,rr,0,1,n));
			}
			else
				scanf("%d",&t),now=t;//将当前时间返回至t
		}
	}
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值