hdu - 4348 - To the moon - 线段树+历史询问

本文详细介绍了如何使用离线操作和持久化数据结构(如线段树)来处理历史查询问题,特别是针对H和B操作的处理方法。通过将操作离线处理和对H询问时间的排序,实现高效查询和更新。同时,讨论了在线操作中引入主席树以应对实时需求。详细解释了B操作的回退机制,以及如何通过维护额外的数组优化回退过程。

据说,关于历史的询问,可以用主席树来做,好像是个函数型的线段树。不懂,等我先把普通线段树历史查询的离线操作搞明白。

意思现在算是理解了。

 可持续化树状数组:

http://www.cnblogs.com/staginner/archive/2012/09/08/2676356.html

http://blog.csdn.net/cyberzhg/article/details/7846729

http://blog.csdn.net/ahfywff/article/details/8018480


虽然是离线的,但是对操作的处理还是按照题给出的顺序处理的。

需要解决的问题是HB

 

对于H,将这类操作全部放入一个数组,按H询问的时间排序。

这样做有什么作用呢?如下:

对于每次的CBt,都去找H询问时间的数组里面相应的t修改一下并记录。

这样做的好处是:

每次按题给的数据顺序询问到H的时间t1时候,记录的就是所需值,输出即可。当C或者B再次修改相同的时间t1时,答案就被覆盖了(此时我们也不需要原来的了)。

为什么要这样做呢:

       因为线段树不是个持久化的数据结构,之后的操作会覆盖之前的,所以当操作到后面的时间时,再去询问之前的是不可能的。所以,对于t时间的CB,都要去修改H里面相应的t的答案。

 

对于B,就将在它之前加入的线段删掉知道相应需要回到的时间即可。

关键在于在它之前。什么叫做在它之前呢?它之前有个B肿么办呢?

 

我们想,这样的序列(B前面还有一个B

12   3   4       5   6    7        8     9         下标

CC C   B(2)  C  C   B(1)   C    C

01   2     3      4   5   6         7    8        应该回去的操作


后面那个B表示回到时间1,前面B表示回去时间2

 

当处理到后面那个B的时候,它一个一个往回退。当退到前面那个B的时候,它不能还一直傻不愣登地一个一个往回退,它应该退到第一个B已经回去的时间点。

方法就是弄个数组,记录i个操作时B时应该回去的操作,这样,回退到第一个B时就直接跳到下标为2的操作了。

 

如下:

12   3   4       5   6    7        8     9         下标

CC C   B(2)  C  C   B(1)   C    C

01   2     2      4   5   6         7    8        应该回去的操作

处理完B(2)的时候,就将B(2)改成2,这样,处理到后面的B的时候,回退的时候就可以直接回退到下标为2的点了(忽略了下标为3的点)。

12   3   4       5   6     7       8     9         下标

CC C   B(2)  C  C   B(1)   C    C

01   2     2      4   5    1        7    8        应该回去的操作


哈哈,那么在线的方法肿么做呢?这就要用到主席树了。待我学会了就写。


 思路就是这样,但是我还没A不了。求帮忙看看哪里错了。您就看在我这样耐心解说的份上,帮我看看吧。您的大恩大德来世再报!

我wa到要死了!!!!


/*
Pro: 0

Sol:

date:
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define havem int m = (l + r) >> 1
#define maxn 1111111
typedef __int64 LL;
using namespace std;
enum {add,que,his,bac};
LL sum[maxn << 2],lazy[maxn << 2],ans[maxn];
int opid[maxn],H[maxn],h , n , m , t;
char op[10];
struct Node{
    int id;
    int l,r,d;
}node[maxn];
void build(int l, int r, int rt){
    lazy[rt] = 0;
    if(l == r){
        scanf("%I64d",&sum[rt]);return ;
    }havem;
    build(lson);    build(rson);
    sum[rt] = sum[rt << 1] + sum[rt << 1| 1];//push_up
}
void input(){
    h = 0;  t = 0; // H[0] = 0;   node[0].d = 0;
    for(int i = 1; i <= m; i ++){
        scanf("%s",op);
        if(op[0] == 'C'){
            node[i].id = add;
            scanf("%d%d%d",&node[i].l, &node[i].r, &node[i].d);
        }else if(op[0] == 'Q'){
            node[i].id = que;
            scanf("%d%d",&node[i].l, &node[i].r);
        }else if(op[0] == 'H'){
            node[i].id = his;
            scanf("%d%d%d",&node[i].l, &node[i].r, &node[i].d);
            H[h ++] = i;
        }else if(op[0] == 'B'){
            node[i].id = bac;
            scanf("%d",&node[i].d);
        }
        opid[i] = i - 1;//应该回去的地方
    }
}
void push_dn(int rt,LL l, LL r){
    if(lazy[rt]){
        sum[rt << 1] += lazy[rt] * ( ((l + r) >> 1) - l + 1);
        sum[rt << 1 | 1] += lazy[rt] * (r - ((l + r) >> 1));
        lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];
        lazy[rt] = 0;
    }
}
void update(int L, int R, int add, int l, int r, int rt){
    if(L <= l && r <= R){
        sum[rt] += (LL)add * (LL)(r - l + 1);
        lazy[rt] += add;    //
        return ;
    }havem;
    push_dn(rt,l,r);
    if(L <= m) update(L,R,add,lson);
    if(R > m)  update(L,R,add,rson);
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
LL query(int L, int R, int l, int r, int rt){
    if(L <= l && r <= R){
        return sum[rt];
    }havem;
    push_dn(rt,l,r);
    __int64 ret = 0;    //
    if(L <= m) ret = query(L,R,lson);
    if(R > m) ret += query(L,R,rson);
    return ret;     //
}
void changehis(int key){
    int low = 0, high = h - 1;
    while(low < high){
        int mid = (low + high) >> 1;
        if(node[H[mid]].d < key) low = mid + 1;//与询问时间比
        else high = mid;
    }

    int x = low;
    while(node[H[x]].d == key && x < h)
        ans[H[x]] = query(node[H[x]].l, node[H[x]].r, 1,n,1), x ++;
}
bool cmp(int a, int b){
    return node[a].d < node[b].d;
}
int main(){
    while(scanf("%d%d",&n,&m) != EOF){
        build(1,n,1);

        memset(H,0,sizeof(H));
        input();
        sort(H,H + h,cmp);//按询问的时间排序


        for(int k=0;k<h;++k)//这是时间为0的时候的处理,因为当时间+1之后也就不能回来了
        {//所以这是必要的!
            if(node[H[k]].d<t)continue;
            if(node[H[k]].d==t)ans[H[k]]=query(node[H[k]].l,node[H[k]].r,1,n,1);
            else break;
        }

        for(int i = 1; i <= m; i ++){//离线处理
            if(node[i].id == add){
                update(node[i].l,node[i].r,node[i].d,1,n,1);  t ++;
                changehis(t);
            }else if(node[i].id == que){
                printf("%I64d\n",query(node[i].l,node[i].r,1,n,1));
            }else if(node[i].id == his){
                printf("%I64d\n",ans[i]);
            }else if(node[i].id == bac){
            // back
                int k = opid[i];

                while(k && t > node[i].d){ // && k?
                    if(node[k].id == add)
                        update(node[k].l, node[k].r, -node[k].d, 1,n,1), t--;
                    k = opid[k];
                } opid[i] = k;
                changehis(t);
            }
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值