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;}


0 0
原创粉丝点击