据说,关于历史的询问,可以用主席树来做,好像是个函数型的线段树。不懂,等我先把普通线段树历史查询的离线操作搞明白。
意思现在算是理解了。
可持续化树状数组:
虽然是离线的,但是对操作的处理还是按照题给出的顺序处理的。
需要解决的问题是H和B。
对于H,将这类操作全部放入一个数组,按H询问的时间排序。
这样做有什么作用呢?如下:
对于每次的C和B的t,都去找H询问时间的数组里面相应的t,修改一下并记录。
这样做的好处是:
每次按题给的数据顺序询问到H的时间t1时候,记录的就是所需值,输出即可。当C或者B再次修改相同的时间t1时,答案就被覆盖了(此时我们也不需要原来的了)。
为什么要这样做呢:
因为线段树不是个持久化的数据结构,之后的操作会覆盖之前的,所以当操作到后面的时间时,再去询问之前的是不可能的。所以,对于t时间的C和B,都要去修改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;
}