HDU 4348 - To the moon

15 篇文章 0 订阅

 

题目地址 :  http://acm.hdu.edu.cn/showproblem.php?pid=4348

 

2012多校第五场,1010题。

 

思路 :  

      其实很简单,线段树的区间查询、区间求和。

      只是两个“查询第 t 次修改时的内容”、“恢复到 t 时刻”操作比较不好想。

      我这里利用的是离线法。

      先将“修改操作”和“查询操作”分别放入栈和优先队列中。

      如果没有遇到 Back 操作,那么我们最后直接将时间 倒着走一遍即可,同时保存结果。

      如果遇到了 Back 操作,也不着急,将时间倒着走到 t 时刻,保存这段时间内的询问的结果。

      然后继续输入新的数据,再遇到 Back,则再次回到 t 时刻,如此往复,直到不再有 Back,直接倒着走完。

      此时,我们已经得到了所有询问的结果。

      按照询问的顺序,依次输出即可。

 

PS :

      比赛的时候按照类似这个的思路去敲,TLE了,额,囧。。。

      同样也是离线的线段树。

      与现在AC的代码相比,比赛时的代码 不同的应该是 Back 部分。

      在当时,Back操作 时,我是将 “询问操作” 拿出队列后,待删除了 t  之后的所有“修改操作”之后,又把“询问操作”重新加回队列。。。

      于是只要 Back 操作 够多,“询问操作”的数量也够多,那么足够让我 TLE 到屎了。。。

      代码改进后,“询问操作” 出了队列之后,就不用再进队列了。

      而相应的,每个“修改操作”需要进行两次,一次是区间加数,另一次是区间减数(即销毁自己,相当于退了1个时间)。

      所以时间提高了许多。。。

      (由于比赛时TLE,所以把STL的优先队列改成了手写堆,但还是TLE。。。  现在这个代码就没把手写堆改回去了,毕竟这个的时间更快嘛,嘿嘿)。

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll (v<<1)
#define rr (v<<1|1)
#define tmid (((l+r)>>1))

using namespace std;

const int maxn=1000030;

int n,m,q;
int s[maxn];
__int64 res[maxn];

//  手写堆 , 用于存储【询问操作】,堆顶为时间t =============== 

struct Ans{
	int l,r,t,i;
	Ans(){};
	Ans(int a,int b,int c,int d){l=a,r=b,i=c,t=d;}
}Q[maxn];

Ans que[maxn];

void add(int v){
	Ans tmp;
	while(v>1){
		if(que[v].t<=que[v>>1].t) return;
		tmp=que[v];
		que[v]=que[v>>1];
		que[v>>1]=tmp;
		v>>=1;
	}
}
void change(int v){
	int l=v<<1;
	Ans tmp;
	while(l<=q){
		if(l+1<=q && que[l+1].t>que[l].t) l++;
		if(que[v].t>=que[l].t) return;
		tmp=que[v];
		que[v]=que[l];
		que[l]=tmp;
		v=l;
		l=v<<1;
	}
}

//  线段树,含有区间更新、查询   ======================

__int64 num[maxn<<2],rec[maxn<<2];

__int64 query(int L,int R,int l,int r,int v){
	if(L<=l && r<=R)
		return num[v];
	if(rec[v]){
		rec[ll]+=rec[v];
		rec[rr]+=rec[v];
		num[ll]+=rec[v]*(tmid-l+1);
		num[rr]+=rec[v]*(r-tmid);
		rec[v]=0;
	}
	__int64 res=0;
	if(L<=tmid) res+=query(L,R,l,tmid,ll);
	if(tmid<R) res+=query(L,R,tmid+1,r,rr);
	return res;
}

void update(int L,int R,int d,int l,int r,int v){
	if(L<=l && r<=R){
		rec[v]+=d;
		num[v]+=(__int64)(r-l+1)*d;
		return;
	}
	if(rec[v]){
		rec[ll]+=rec[v];
		rec[rr]+=rec[v];
		num[ll]+=rec[v]*(tmid-l+1);
		num[rr]+=rec[v]*(r-tmid);
		rec[v]=0;
	}
	if(L<=tmid) update(L,R,d,l,tmid,ll);
	if(tmid<R) update(L,R,d,tmid+1,r,rr);
	num[v]=num[ll]+num[rr];
}

void make_tree(int l,int r,int v){
	rec[v]=0;
	if(l==r){
		num[v]=s[l];
		return;
	}
	make_tree(l,tmid,ll);
	make_tree(tmid+1,r,rr);
	num[v]=num[ll]+num[rr];
}

// back操作,回到 t 时刻  ======================

struct Cge{ // 用栈C[] ,存储 【修改操作】  
	int l,r,d;
	Cge(){};
	Cge(int a,int b,int c){l=a,r=b,d=c;}
}C[maxn];

void clr(int t,int tol){
	while(tol>t){
		while(q>0)
			if(que[1].t>=tol){
	//			cout<<"Q: ( "<<que[1].l<<" , "<<que[1].r<<" ) --- "<<que[1].i<<endl;
				res[que[1].i]=query(que[1].l,que[1].r,1,n,1);
	//			cout<<"res: "<<res[que[1].i]<<endl;
				que[1]=que[q--];
				change(1);
			}
			else break;
	//	cout<<"C: ( "<<C[tol].l<<" , "<<C[tol].r<<" ) --- "<<tol<<endl;
		update(C[tol].l,C[tol].r,-C[tol].d,1,n,1);
		tol--;
	} 
}

//  主函数  ====================================

int main(){
	int i,j,tol,now,l,r,d,dis,cnt,t,step;
	char op[3];
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
		scanf("%d",&s[i]);
	make_tree(1,n,1);
	tol=0;
	step=0;
	q=0;
	C[0]=Cge(1,n,0);
	while(m--){
		scanf("%s",op);
		if(op[0]=='C'){
			scanf("%d%d%d",&l,&r,&d);
			C[++tol]=Cge(l,r,d);
			update(l,r,d,1,n,1);
		}
		else if(op[0]=='Q'){
			scanf("%d%d",&l,&r);
			que[++q]=Ans(l,r,step++,tol);
			add(q);
		}
		else if(op[0]=='H'){
			scanf("%d%d%d",&l,&r,&t);
			que[++q]=Ans(l,r,step++,t);
			add(q);
		}
		else if(op[0]=='B'){
			scanf("%d",&t);
			clr(t,tol);
			tol=t;
		}
	}
	clr(-1,tol);
	for(i=0;i<step;i++)
		printf("%I64d\n",res[i]);
	return 0;
}


/*

==================
2 3
0 0
C 1 2 1
B 1
Q 1 2

---------------
2

========================
10 5
0 0 0 0 0 0 0 0 0 0
C 1 10 1
C 1 10 1
B 0
C 1 10 2
H 1 10 1

---------------
20

========================
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

-----------------
4
55
9
15

========================
2 4
0 0
C 1 1 1
C 2 2 -1
Q 1 2
H 1 2 1

-----------------
0
1

*/


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值