hdu4967 线段树
来源:互联网 发布:mac系统qq不能远程 编辑:程序博客网 时间:2024/06/05 02:15
题意:对一个栈,有三种操作:pop,push,peak(查询当前栈顶元素的值)。但在此题中,每个操作都有时间戳。操作按顺序给出,但如果后面的操作的时间戳小,会把之前,大于该时间戳的所有操作全部取消,即恢复到该时间戳时的状态,执行完该操作后,再把取消的操作重新执行。
但,peak操作不会重新执行,即,对于给定的peak操作,它也会把大于它的时间戳的操作取消,完成自己的查询操作。但,对于后来的小于该时间戳的操作,不会重新执行这个peak操作。(可以认为,这个打印操作只会执行一次。)
思路:只是简单的按照时间排序是行不通的。因为peak操作只执行一次,它对前后操作的依赖关系很大,只按时间排序后,会把依赖关系的信息破坏掉。我们只能按照给定的操作顺序去处理。
我们可以利用线段树去解决这道题。
首先,因为时间的范围很大,而总的操作数很少,我们首先离散化时间。
然后:对于每个push操作,我们对对应的时间点+1,pop操作,我们对对应的时间点-1;
而peak操作,设对应的时间点为t,在这种情况下,就等价于,找出一个最大的时间点t',使 [ t', t)区间的和大于0(大于0保证栈不为空,最大的时间点保证是在当前时间点下,栈顶的元素。)
那,如何去求出这个最大的时间点t'呢?或者说,线段树该保存怎样的信息?
每个节点对应区间的区间和,这个是一定要有的。但我们如果只保存区间和,会发现,如果多次求不同区间的和,实际上会大大增加复杂度。
因为我们要求的是区间[t',t)的起点位置,而这个区间和,也相当于以t'为起点的后缀和,所以我们还要维护每个节点对应区间的最大后缀和。
最大后缀和的维护公式:
设节点为o,左儿子为ls,右儿子为rs,区间和为sum,最大后缀和为rsum,则 rum[o] = max(rsum[r], rsum[l] + sum[r]),即最大后缀和的起点要么位于右儿子对应区间,要么位于左儿子对应区间。
我们再回头来看如何求出t'。
我们可以利用线段树,将[1,t)分成log(t)个不相交的区间(分的方法就是递归调用,直到完全覆盖),从右到左,每个区间分开考虑,同时有一个变量v保存考虑过的区间的和。对于每个区间o:
1.如果v+ rsum[0] <= 0, 说明在这个区间内,无法找到t',那么 累加v = v + sum[o],继续向左考虑。
2.如果v + rsum[o] > 0,说明在这个区间内,存在某个时间点t',使[t',t)区间和大于0。由步骤1可知,这个区间右面的所有区间都不满足,而该区间满足,所以t'一定是最大的。
然后递归考虑这个区间,直到找到准确的时间点为止。
3.如果已经到了最左端仍然没满足要求,说明不存在。
这样,处理这个题的整体思路就很清晰了。
代码如下:
#include<iostream>#include<string>#include<cstring>#include<cstdio>#include<cmath>#include<iomanip>#include<map>#include<algorithm>#include<queue>#include<set>#define inf 1000000000#define pi acos(-1.0)#define eps 1e-8#define seed 131using namespace std;typedef pair<int,int> pii;typedef unsigned long long ull;typedef long long ll;const int maxn=50005;struct Node{ int state; int x,ti;}node[maxn];int cpy[maxn];int n;int d[maxn];int sum[maxn<<2];int hou[maxn<<2];int tot,ans;void pushup(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; hou[rt]=max(hou[rt<<1|1],sum[rt<<1|1]+hou[rt<<1]);}void update(int L,int u,int l,int r,int rt){ if(l==r) { sum[rt]+=u; hou[rt]+=u; return; } int m=(l+r)>>1; if(L<=m) update(L,u,l,m,rt<<1); if(L>m) update(L,u,m+1,r,rt<<1|1); pushup(rt);}void solve(int l,int r,int rt){ if(l==r) { ans=l; return; } int m=(l+r)>>1; if(tot+hou[rt<<1|1]>0) solve(m+1,r,rt<<1|1); else { tot+=sum[rt<<1|1]; solve(l,m,rt<<1); }}void query(int L,int R,int l,int r,int rt){ if(ans!=-1) return; if(L<=l&&R>=r) { if(tot+hou[rt]>0) solve(l,r,rt); else tot+=sum[rt]; return; } int m=(l+r)>>1; if(R>m) query(L,R,m+1,r,rt<<1|1); if(L<=m) query(L,R,l,m,rt<<1);}int main(){ string s; int a,b; int cas=1; while(~scanf("%d",&n)&&n) { for(int i=0;i<n;i++) { cin>>s; if(s=="push") { scanf("%d%d",&node[i].x,&node[i].ti); node[i].state=1; } else if(s=="pop") { scanf("%d",&node[i].ti); node[i].state=2; } else { scanf("%d",&node[i].ti); node[i].state=3; } cpy[i]=node[i].ti; } sort(cpy,cpy+n); for(int i=0;i<n;i++) { node[i].ti=lower_bound(cpy,cpy+n,node[i].ti)-cpy+1; d[node[i].ti]=node[i].x; } printf("Case #%d:\n",cas++); memset(sum,0,sizeof(sum)); memset(hou,0,sizeof(hou)); for(int i=0;i<n;i++) { if(node[i].state==1) update(node[i].ti,1,1,n,1); else if(node[i].state==2) update(node[i].ti,-1,1,n,1); else { tot=0;ans=-1; query(1,node[i].ti,1,n,1); if(ans==-1) printf("-1\n"); else printf("%d\n",d[ans]); } } } return 0;}
- hdu4967 线段树
- hdu4967
- 线段树?线段树!
- 线段树?线段树!
- 线段_线段树
- 线段_线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- Android SDK 快速下载
- 微信jssdk .net 版本
- 4. ipsec-tools on ubuntu14.04
- FFmpeg常用基本命令
- Python学习笔记(一)
- hdu4967 线段树
- DASH Streaming Support
- asp.net 利用Web.config实现整站301永久重定向
- Android自定义权限
- Object_c基础——getter、setter方法和@property、self关键字
- ngx_http_fastcgi_module的那些事:
- Android 开发之 ---- 底层驱动开发(二)
- [leetcode][list] Copy List with Random Pointer
- ubuntu 12.04安装chrome