1. 程式人生 > >HDU 4348 To the moon(主席樹區間修改)

HDU 4348 To the moon(主席樹區間修改)

歷史 ons 證明 spa 產生 sin Go != 註意

題意

給你一個區間,支持如下操作:

  • 在一段區間內加上一個值,並生成一個歷史版本
  • 查詢某個版本下一段區間內的和
  • 回到一個歷史版本上並舍棄之後的版本

做法

這就是主席樹區間修改裸題啦QwQ

上一篇博客我講了主席樹可以資瓷單點修改,那麽區間修改資不資瓷呢?那當然是資瓷的啦。

就像一般的線段樹一樣,主席樹的一個內部節點也可以存儲兩個兒子的和,在修改時打標記,在查詢時直接返回當前區間的值即可。和單點修改不同的是,區間修改一次最多需要修改線段樹上的4個節點(可以腦內證明),也就是說我們需要在歷史版本上新建 $ 4log_2n $ 個節點。這個過程在單點修改的基礎上稍加修改就可以實現,非常簡單。

這時候,你可能會發現一個問題:標記下放時,新版本的標記不就下放到舊版本上去了嗎(因為被打標記的區間的兩個兒子都指向著歷史版本)?

一個解決方法是:每一次下放都新建兩個節點以防止標記汙染到歷史版本。這個可行度很高,然而QwQ...這道題的空間只有狗日的64MB!!!這麽做空間吃不消啊(╯‵□‘)╯︵┻━┻

還有一個更直接的方法是:標記根本就不用下放!由於加法標記是可以累加的,在查詢時只要把一路上的標記累加起來,再乘以當前區間長度即可。註意,pushup時不能直接讓該節點的值等於子節點的值的和,還要加上當前標記乘以區間長度的值,這樣才能讓當前節點的標記對它上面的節點產生影響。

事實上,標記永久化也可以用在一般的線段樹中。但是一定要註意,標記必須要是可累加的且與順序無關的。比如又有加法又有乘法就不行。

代碼

一定要註意輸出格式啊QwQ

//By sclbgw7
#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register
using namespace std;
typedef long long LL;
const int MAXN=100005;
int a[MAXN],n;

template<class T>int read(T &x)//這是可以判EOF的快速讀入
{
    x=0;int ff=0;char ch=getchar();
    while((ch<‘0‘||ch>‘9‘)&&ch!=EOF){ff|=(ch==‘-‘);ch=getchar();}
    if(ch==EOF)return 0;
    while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=ff?-x:x;
    return 1;
}

void readc(char &ch)
{
    ch=getchar();
    while(ch<‘A‘||ch>‘Z‘)ch=getchar();
}

class CMT
{
    private:
    
    int cnts[MAXN],root[MAXN],cnt,now;
    
    struct CMT_node
    {
        int l,r,tag;
        LL x;
    }node[MAXN*30];
    
    void build(int l,int r,int &x)
    {
        x=++cnt;node[x].tag=0;//一定要清零
        if(l==r){node[cnt].x=a[l];return;}
        int m=(l+r)>>1;
        build(l,m,node[x].l);
        build(m+1,r,node[x].r);
        node[x].x=node[node[x].l].x+node[node[x].r].x;
    }
    
    void insert(int st,int en,int l,int r,int &x,int y,int del)
    {
        x=++cnt,node[x]=node[y];
        if(st<=l&&en>=r)
        {
            node[x].tag+=del;
            node[x].x+=(LL)((r-l+1)*del);
            return;
        }
        int m=(l+r)>>1;
        if(st<=m)insert(st,en,l,m,node[x].l,node[y].l,del);
        if(en>m)insert(st,en,m+1,r,node[x].r,node[y].r,del);
        node[x].x=node[node[x].l].x+node[node[x].r].x+(LL)(r-l+1)*node[x].tag;  //pushup
    }
    
    LL query(int st,int en,int l,int r,int x,int tsum)
    {
        if(st<=l&&en>=r)
        {
            LL ans=(LL)((r-l+1)*tsum);
            ans+=node[x].x;
            return ans;
        }
        int m=(l+r)>>1;LL ans=0;
        if(st<=m)ans+=query(st,en,l,m,node[x].l,tsum+node[x].tag);
        if(en>m)ans+=query(st,en,m+1,r,node[x].r,tsum+node[x].tag);
        return ans;
    }
    
    public:
    
    void init()
    {
        cnt=now=0;
        build(1,n,root[0]);
        cnts[0]=cnt;
    }
    
    void back(int x)//為了節省空間,回退時釋放內存
    {
        now=x;
        cnt=cnts[now];
    }
    
    void change(int l,int r,int del)
    {
        ++now;
        insert(l,r,1,n,root[now],root[now-1],del);
        cnts[now]=cnt;
    }
    
    LL getsum(int l,int r,int his,int isnow=0)
    {
        if(isnow)return query(l,r,1,n,root[now],0);
        return query(l,r,1,n,root[his],0);
    }
}cmt;

int main()
{
    int m,fl=0;
    while(read(n))
    {
        if(fl)printf("\n");
        else fl=1;
        read(m);
        for(R int i=1;i<=n;++i)
          read(a[i]);
        cmt.init();
        char ch;
        int t1,t2,t3;
        for(R int i=1;i<=m;++i)
        {
            readc(ch);
            if(ch==‘C‘)
            {
                read(t1),read(t2),read(t3);
                cmt.change(t1,t2,t3);
            }
            else if(ch==‘Q‘)
            {
                read(t1),read(t2);
                printf("%lld\n",cmt.getsum(t1,t2,0,1));
            }
            else if(ch==‘H‘)
            {
                read(t1),read(t2),read(t3);
                printf("%lld\n",cmt.getsum(t1,t2,t3));
            }
            else
            {
                read(t1);
                cmt.back(t1);
            }
        }
    }
    return 0;
}

HDU 4348 To the moon(主席樹區間修改)