【HDU4348】To The Moon-主席树(可持久化线段树)区间修改+区间询问

测试地址:To The Moon

题目大意:维护一个长度为N的数列,支持以下操作:将其中的某个区间内元素加上一个值然后将时间戳+1,询问当前时间戳内某一个区间内元素的和,询问某个时间戳内某一个区间内元素的和,将时间戳重置回某一个前面的时间。

做法:这道题一看就是主席树了,按照主席树区间修改和区间询问的方法去做就行了。但是这道题有点卡空间,如果空间爆了可以用以下方式减少空间的用量:不用下放标记,只需要在询问时实时记录从根到某个节点路径上所有标记的和,就相当于覆盖在这个节点上的标记了,但是要特别注意这种情况下的pushup,在pushup时不要忘了加上该区间原来的标记即可。最后就是注意数据可能爆int,要用long long来存储区间和。

题外话:To The Moon这个游戏我当初玩过,在做题时又看到这个还是挺兴奋的^_^,据说还是国人作品,很厉害啊~

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,a[100010],rt[100010],tot,now;
struct segnode
{
  int lc,rc;
  long long sum,p;
}seg[4000010];

void buildtree(int &no,int l,int r)
{
  no=++tot;
  seg[no].lc=seg[no].rc=seg[no].p=0;
  if (l==r)
  {
    seg[no].sum=a[l];
    return;
  }
  int mid=(l+r)>>1;
  buildtree(seg[no].lc,l,mid);
  buildtree(seg[no].rc,mid+1,r);
  seg[no].sum=seg[seg[no].lc].sum+seg[seg[no].rc].sum;
}

void modify(int &no,int last,int l,int r,int s,int t,long long c)
{
  no=++tot;
  seg[no]=seg[last];
  if (l>=s&&r<=t)
  {
    seg[no].p+=c;
	seg[no].sum+=c*(r-l+1);
	return;
  }
  int mid=(l+r)>>1;
  if (s<=mid) modify(seg[no].lc,seg[last].lc,l,mid,s,t,c);
  if (t>mid) modify(seg[no].rc,seg[last].rc,mid+1,r,s,t,c);
  seg[no].sum=seg[seg[no].lc].sum+seg[seg[no].rc].sum+(r-l+1)*seg[no].p;
}

long long query(int no,int l,int r,int s,int t,long long presum)
{
  if (l>=s&&r<=t) return seg[no].sum+presum*(r-l+1);
  int mid=(l+r)>>1;
  long long sums=0;
  if (s<=mid) sums+=query(seg[no].lc,l,mid,s,t,presum+seg[no].p);
  if (t>mid) sums+=query(seg[no].rc,mid+1,r,s,t,presum+seg[no].p);
  return sums;
}

int main()
{
  int t=0;
  while(scanf("%d%d",&n,&m)!=EOF)
  {
    t++;
	if (t>1) printf("\n");
    tot=now=0;
    for(int i=1;i<=n;i++)
      scanf("%d",&a[i]);
    buildtree(rt[0],1,n);
    
    for(int i=1;i<=m;i++)
    {
      char op[10];
	  int a,b,c;
	  scanf("%s",op);
	  if (op[0]=='C')
	  {
	    scanf("%d%d%d",&a,&b,&c);
		modify(rt[now+1],rt[now],1,n,a,b,c);
		now++;
	  }
	  if (op[0]=='Q')
	  {
	    scanf("%d%d",&a,&b);
		printf("%lld\n",query(rt[now],1,n,a,b,0));
	  }
	  if (op[0]=='H')
	  {
	    scanf("%d%d%d",&a,&b,&c);
		printf("%lld\n",query(rt[c],1,n,a,b,0));
	  }
	  if (op[0]=='B')
	  {
	    scanf("%d",&a);
		now=a;
	  }
    }
  }
  
  return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值