HDU 4348 To the moon (主席树区间更新)

题意:首先给你n个数,开始时间为0,最后按照操作输出

给你四种操作:

1. C l r d :  在(l,r)区间都加上d,时间加一
2. Q l r :  询问现在(l,r)的区间和
3. H l r t :  询问在t的时间(l,r)的区间和
4. B t : 直接回到t的时间

 

题解:首先是区间修改区间查询,可以想到线段树,接着就是询问历史版本与回到历史版本,这样就是主席树了

首先我们知道普通主席树是单点修改,并支持历史版本的区间求和与回到历史版本(就是这删除之后的树),仅仅只是因为它存了多棵线段树

而我们这儿是要进行区间修改,所以第一反应就是模拟线段树的lazy标记,并在查询时再更新再建树,但是这样会卡空间

因此我们需要这样想,模拟lazy标记进行重建树(最多建立2*log2(n)个节点)是必须的,但是查询时就不需要重建树了

这样我们就需要记录两个值:sum代表这一段中被增加区间的与此这一段的区间相交的总和,com代表这一段都需要增加这么多

这时我们查询时就需要每次加上com与待查询的区间相交的值(加上之前更新的),最后再包含的区间里加上sum再减去这儿com与待查询的区间相交的值(重复了)

 

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=1<<28;
const ll INF=1ll<<60;
const double Pi=acos(-1.0);
const int Mod=1e9+7;
const int Max=1e5+7;
int root[Max],tot;
struct node
{
    int lef,rig;
    ll sum,com;//相交总共的和 这一段每个点都加上这个
}msegtr[Max*40];
ll pre[Max];//把原来的数放入前缀和里,主席树里仅仅更新修改的值
void Init(int &cnt)
{
    cnt=0;
    msegtr[0].lef=msegtr[0].rig=0;
    msegtr[0].sum=msegtr[0].com=0ll;
    root[0]=0,tot=0;
    pre[0]=0ll;
    return;
}
int Jud(int sta,int enn,int lef,int rig)//关键的区间交
{
    return max(min(rig,enn)-max(lef,sta)+1,0);
}
void Create(int sta,int enn,int &x,int y,int lef,int rig,int com)
{
    msegtr[++tot]=msegtr[y];
    msegtr[tot].sum+=(ll)com*Jud(sta,enn,lef,rig);
    x=tot;
    if(sta>=lef&&enn<=rig)//模拟线段树区间更新
    {
        msegtr[tot].com+=com;//这一段每个点都加上这个
        return;
    }
    int mid=dir(sta+enn,1);
    if(mid>=lef)
        Create(sta,mid,msegtr[x].lef,msegtr[y].lef,lef,rig,com);
    if(mid<rig)
        Create(mid+1,enn,msegtr[x].rig,msegtr[y].rig,lef,rig,com);
    return;
}
ll Query(int sta,int enn,int x,int lef,int rig)
{
    if(sta>=lef&&enn<=rig)
    {
        return msegtr[x].sum-msegtr[x].com*Jud(sta,enn,lef,rig);//多加了,要减去
    }
    int mid=dir(sta+enn,1);
    ll ans=0ll;
    if(mid>=lef)
    {
        ans+=Query(sta,mid,msegtr[x].lef,lef,rig)+msegtr[msegtr[x].lef].com*Jud(sta,mid,lef,rig);//加上区间交的值
    }
    if(mid<rig)
    {
        ans+=Query(mid+1,enn,msegtr[x].rig,lef,rig)+msegtr[msegtr[x].rig].com*Jud(mid+1,enn,lef,rig);
    }
   return ans;
}
int main()
{
    int n,m,temp,cnt;
    int lef,rig,sum;
    char str[10];
    while(~scanf("%d %d",&n,&m))
    {
        Init(cnt);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&temp);
            pre[i]=pre[i-1]+temp;
        }
        for(int i=0;i<m;++i)
        {
            scanf("%s",str);
            if(str[0]=='C')
            {
                scanf("%d %d %d",&lef,&rig,&sum);
                Create(1,n,root[cnt+1],root[cnt],lef,rig,sum);
                cnt++;
            }
            else if(str[0]=='Q')
            {
                scanf("%d %d",&lef,&rig);
                printf("%I64d\n",Query(1,n,root[cnt],lef,rig)+pre[rig]-pre[lef-1]);
            }
            else if(str[0]=='H')
            {
                scanf("%d %d %d",&lef,&rig,&sum);
                printf("%I64d\n",Query(1,n,root[sum],lef,rig)+pre[rig]-pre[lef-1]);
            }
            else
            {
                scanf("%d",&sum);
                if(sum<cnt)
                {
                    tot=root[sum+1];
                    cnt=sum;//存储线段树,这样就代表摧毁了后面更新的树
                }
            }
        }
    }
    return 0;
}

 

posted @ 2016-10-06 13:52  专注如一  阅读(971)  评论(0编辑  收藏  举报