蓝书(算法竞赛进阶指南)刷题记录——hdu4348 To the moon(主席树)

题目:hdu4348.
题目大意:给定一个长度为 n n n的序列,要求支持以下操作 m m m次:
1.格式 C   l   r   d C\,l\,r\,d Clrd,表示给区间 [ l , r ] [l,r] [l,r]加上 d d d并给时刻 t t t 1 1 1.
2.格式 Q   l   r Q\,l\,r Qlr,表示查询当前区间 [ l , r ] [l,r] [l,r]的和.
3.格式 H   l   r   t H\,l\,r\,t Hlrt,表示查询时刻 t t t时区间 [ l , r ] [l,r] [l,r]的和.
4.格式 B   t B\,t Bt,表示回到时刻 t t t.
1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq 10^5 1n,m105.

这道题看起来可以直接可持久化搞,但是有一个问题是,如果打标机的话最多会影响 O ( n ) O(n) O(n)级别的节点,即使打上懒标记还是会在下传的时候出现问题,怎么办呢?

其实有一种技巧叫做永久化标记,就是打的标记不下传,查询的时候直接把顺路的标记也统计进去.

有了这种技巧,每次修改我们最多只会动用 O ( log ⁡ n ) O(\log n) O(logn)个节点,这个时候我们就可以持久化了,具体可持久化过程与单点修改类似.

时空复杂度 O ( n + m log ⁡ n ) O(n+m\log n) O(n+mlogn),建议最好开 4 n log ⁡ n 4n\log n 4nlogn的空间,因为线段树区间修改最多会动用 4 log ⁡ n 4\log n 4logn个节点,当然明显跑不满,实测开 30 n 30n 30n即可.

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,C=20;

char rc(){
  char c=getchar();
  for (;c<'A'||c>'Z';c=getchar());
  return c;
}

int n,m;
LL a[N+9];
struct tree{
  LL sum,tag;
  int s[2];
}tr[N*C*4+9];
int cn,rot[N+9];

void Build(int L,int R,int &k=rot[0]){
  tr[k=++cn]=tree();
  if (L==R){tr[k].sum=a[L];return;}
  int mid=L+R>>1;
  Build(L,mid,tr[k].s[0]);Build(mid+1,R,tr[k].s[1]);
  tr[k].sum=tr[tr[k].s[0]].sum+tr[tr[k].s[1]].sum;
}

void Add_tree(int L,int R,LL v,int l,int r,int hk,int &k){
  tr[k=++cn]=tr[hk];
  tr[k].sum+=v*(R-L+1);
  if (l==L&&r==R){tr[k].tag+=v;return;}
  int mid=l+r>>1;
  if (R<=mid) Add_tree(L,R,v,l,mid,tr[hk].s[0],tr[k].s[0]);
  else if (L>mid) Add_tree(L,R,v,mid+1,r,tr[hk].s[1],tr[k].s[1]);
    else Add_tree(L,mid,v,l,mid,tr[hk].s[0],tr[k].s[0]),Add_tree(mid+1,R,v,mid+1,r,tr[hk].s[1],tr[k].s[1]);
}

LL Query_sum(int L,int R,int l,int r,int k){
  if (l==L&&r==R) return tr[k].sum;
  int mid=l+r>>1;
  LL res=tr[k].tag*(R-L+1);
  if (R<=mid) return Query_sum(L,R,l,mid,tr[k].s[0])+res;
  else if (L>mid) return Query_sum(L,R,mid+1,r,tr[k].s[1])+res;
    else return Query_sum(L,mid,l,mid,tr[k].s[0])+Query_sum(mid+1,R,mid+1,r,tr[k].s[1])+res; 
}

Abigail into(){
  for (int i=1;i<=n;++i)
    scanf("%lld",&a[i]);
}

Abigail work(){
  cn=0;
  Build(1,n);
}

Abigail getans(){
  int l,r,d,t=0;
  while (m--)
    switch (rc()){
      case 'C':
      	scanf("%d%d%d",&l,&r,&d);
      	++t;
      	Add_tree(l,r,(LL)d,1,n,rot[t-1],rot[t]);
      	break;
      case 'Q':
      	scanf("%d%d",&l,&r);
      	printf("%lld\n",Query_sum(l,r,1,n,rot[t]));
      	break;
      case 'H':
      	scanf("%d%d%d",&l,&r,&d);
      	printf("%lld\n",Query_sum(l,r,1,n,rot[d]));
      	break;
      case 'B':
      	scanf("%d",&t);
      	cn=rot[t+1]-1;
      	break;
	}
}

int main(){
  int cas=0;
  while (~scanf("%d%d",&n,&m)){
    into();
    work();
    if (cas) puts("");
    ++cas;
    getans();
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值