1. 程式人生 > >HDU 4348 To the moon 【主席樹+區間修改】

HDU 4348 To the moon 【主席樹+區間修改】

persistent segment tree
題目連結

題意

給一串初始序列An,並且初始的時間是0,定義以下操作:
1. 給一個區間內的數加上一個值,並且時間加一
2. 查詢當前某區間的區間和
3. 查詢過去某個時間的某個區間和
4. 回到某個時間

序列大小和查詢數量級為1e5

分析

SPOJ上也有這個題,但HDU卡記憶體嚴格一些,所以有些方法就不能過了。
首先既然有歷史版本,那麼就用主席樹吧。這裡主席樹中更新的一個版本就是上一個版本的線段樹進行一次區間修改。平常寫線段樹遇到區間修改一般是要用lazytag存一下每個整個結點的變化量,然後有需要訪問子結點時再pushdown下來。但是,如果在主席樹中仿照這樣做,每需要訪問子結點都pushdown一下並建立新節點的話,肯定會爆記憶體的,因為極端情況下,這樣的主席樹最終就會變成n棵線段樹。
其實關於區間修改的線段樹還有另一種寫法:同樣記錄lazytag,但不pushdown,而是在每次查詢的時候記錄當前查詢鏈中的遇到的所有lazytag的和,最終加在那個末尾的查詢結點的返回值上。線段樹中這兩種寫法其實是沒有太大的效果差異的,但在主席樹中就不同了,顯然後者在遇到中間結點更改時,之後的訪問不需要新增子結點,從而大大減少空間消耗。

AC程式碼

//HDU 4348 To the moon
//AC 2016-11-15 17:39:11
//persistent segment tree
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <string>
#include <map> #include <queue> #include <deque> #include <list> #include <sstream> #include <stack> using namespace std; #define cls(x) memset(x,0,sizeof x) #define inf(x) memset(x,0x3f,sizeof x) #define neg(x) memset(x,-1,sizeof x) #define ninf(x) memset(x,0xc0,sizeof x)
#define st0(x) memset(x,false,sizeof x) #define st1(x) memset(x,true,sizeof x) #define lowbit(x) x&(-x) #define input(x) scanf("%d",&(x)) #define inputt(x,y) scanf("%d %d",&(x),&(y)) #define bug cout<<"here"<<endl; //#pragma comment(linker, "/STACK:1024000000,1024000000")//stack expansion //#define debug const double PI=acos(-1.0); const int INF=0x3f3f3f3f;//1061109567-2147483647 const long long LINF=0x3f3f3f3f3f3f3f3f;//4557430888798830399-9223372036854775807 const int maxn=100000+100; int n,m; long long A[maxn]; struct chairmanTree { struct chairNode { long long sum; int lazy; int ls,rs; }tree[maxn*80]; int root[maxn]; size_t tsize; void setup() { tsize=1; root[0]=0; tree[0].ls=tree[0].rs=tree[0].sum=tree[0].lazy=0; return; } void update(int c,int s,int t,int L,int R,int d) { root[c]=insert(root[c-1],s,t,L,R,d); return; } int insert(int x,int s,int t,int L,int R,int d) { tree[tsize++]=tree[x]; x=tsize-1; tree[x].sum+=d*1LL*(t-s+1); if(s==L&&t==R) { tree[x].lazy+=d; return x; } int mid=(L+R)>>1; if(t<=mid) tree[x].ls=insert(tree[x].ls,s,t,L,mid,d); else if(s>mid) tree[x].rs=insert(tree[x].rs,s,t,mid+1,R,d); else { tree[x].ls=insert(tree[x].ls,s,mid,L,mid,d); tree[x].rs=insert(tree[x].rs,mid+1,t,mid+1,R,d); } return x; } long long querry(int x,int s,int t,int L,int R,int tag) { if(s==L&&t==R) return tree[x].sum+(R-L+1)*1LL*tag; int mid=(L+R)>>1; if(t<=mid) return querry(tree[x].ls,s,t,L,mid,tag+tree[x].lazy); else if(s>=mid+1) return querry(tree[x].rs,s,t,mid+1,R,tag+tree[x].lazy); else return querry(tree[x].ls,s,mid,L,mid,tag+tree[x].lazy)+querry(tree[x].rs,mid+1,t,mid+1,R,tag+tree[x].lazy); } }cT; int main() { //ios::sync_with_stdio(false); //cin.tie(0); #ifdef debug freopen("E:\\Documents\\code\\input.txt","r",stdin); freopen("E:\\Documents\\code\\output.txt","w",stdout); #endif //IO bool first=1; while(inputt(n,m)!=EOF) { if(!first) putchar('\n'); first=0; for(int i=1;i<=n;++i) { scanf("%lld",&A[i]); A[i]+=A[i-1]; } cT.setup(); char opr[2]; int a,b,c; int cur=0; while(m--) { scanf("%s",opr); if(opr[0]=='C') { inputt(a,b);input(c); ++cur; cT.update(cur,a,b,1,n,c); } if(opr[0]=='Q') { inputt(a,b); printf("%lld\n",cT.querry(cT.root[cur],a,b,1,n,0)+A[b]-A[a-1]); } if(opr[0]=='H') { inputt(a,b);input(c); printf("%lld\n",cT.querry(cT.root[c],a,b,1,n,0)+A[b]-A[a-1]); } if(opr[0]=='B') input(cur); } } return 0; }