vjudge: spoj--to the moon(主席树区间修改)

本文介绍了一道关于可持久化线段树的经典题目,详细解析了算法思想及实现细节,包括如何处理区间修改、查询操作以及如何通过标记永久化来优化节点创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

       题目大意:给定一些数字,有以下几个操作

       a、[L,R]区间的数字变化D的数值。并且时间向前推进

       b、询问某个时间的[L,R]区间的数字之和,这不花费时间

       c、回到X时间。

 

 

可持久话线段树裸题。

 

其实区间修改就体现了,只要主席树更新的节点,就要重建。

就本题而言,区间加我可以搞成标记永久化,防止pushdown新建太多的节点,感觉尽量不要通过pushdown加太多节点,想办法搞成标记永久化

 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;
const int N=100005;
typedef long long ll;

struct aa
{
	int lc,rc,l,r;
	ll add,sum;
	int len(){return r-l+1;}
}a[N*4*200];

int n,m,rt[N],time,tot;

void build(int &u,int l,int r)
{
	u=++tot;
	a[u].l=l,a[u].r=r;
	if (l==r) {scanf("%lld",&a[u].sum);return ;}
	int mid=(l+r)>>1;
	build(a[u].lc,l,mid);
	build(a[u].rc,mid+1,r);
	a[u].sum=a[a[u].lc].sum+a[a[u].rc].sum;
}
void insert(int &now,int old,int l,int r,ll d)
{
	now=++tot;
	a[now]=a[old];
	if (a[old].l==l&&a[old].r==r)
	{
		a[now].add+=d;
		return;
	}
	a[now].sum+=d*(r-l+1);
	int mid=(a[now].l+a[now].r)>>1;
	if (r<=mid) insert(a[now].lc,a[old].lc,l,r,d);
	else if (mid<l) insert(a[now].rc,a[old].rc,l,r,d);
	else 
	{
		insert(a[now].lc,a[old].lc,l,mid,d);
		insert(a[now].rc,a[old].rc,mid+1,r,d);
	}
}
ll query(int u,int l,int r)
{
	ll tmp=a[u].add*(r-l+1);
	if (a[u].l==l&&a[u].r==r) return a[u].sum+tmp;
	int mid=(a[u].l+a[u].r)>>1;
	if (r<=mid) return tmp+query(a[u].lc,l,r);
	else if (mid<l) return tmp+query(a[u].rc,l,r);
	return tmp+query(a[u].lc,l,mid)+query(a[u].rc,mid+1,r);
}
int back[N];
int main()
{
	scanf("%d%d",&n,&m);
	build(rt[0],1,n);
	time=0;
	char ch[2];
	int x,y,t;ll d;
	
	for (int i=1;i<=m;i++)
	{
		scanf("%s",ch);
		switch(ch[0])
		{
			case 'C':scanf("%d%d%lld",&x,&y,&d);time++;insert(rt[time],rt[time-1],x,y,d);back[time]=tot;break;
			case 'Q':scanf("%d%d",&x,&y);printf("%lld\n",query(rt[time],x,y));break;
			case 'H':scanf("%d%d%d",&x,&y,&t);printf("%lld\n",query(rt[t],x,y));break;
			case 'B':scanf("%d",&time);tot=back[time];break;
		}
	}
	return 0;
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值