HDU 4553 线段树 连续区间和问题

HDU1540题目大意:查找是否有连续的空余时间

他的操作有三个 :

ds x  查找屌丝的时间有连续的x长度的空余时间

ns  x 为女神安排时间,先查找屌丝时间是否有空余,没有的话无视屌丝,直接判断女神时间是否有连续x长的空余时间

stduy a b  清空a到b区间的事情

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N= 1e5+10;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lson id<<1
#define rson id<<1|1
//区间连续和问题,需要考虑一下区间最大值,最大前缀和,最大后缀和
//这题有点区别与简单的线段树区间和问题,因为是这里有两个独立互斥的时间点,它们之间是有优先级的
//因此需要建立两棵树,如果建立在同一棵树中,那么在题目中基友的操作会覆盖掉女神的操作,但是女神的
//操作等级时高于基友的,这样就需要分开,当对女神分时间段时就需要将女神的线段树还有基友的线段树都需要修改
//对基友的操作就只对基友的线段树修改可以了
int n,m;
struct node{
    int l,r,setv;
    int sum,suf,pre;//求区间连续长度问题,当然就是最大连续长度,最大后缀长度,最大后缀长度
}a[N<<2][2];//0代表屌丝的线段树,1代表女神的线段树
void build(int id,int l,int r){
    a[id][0].l=l;a[id][0].r=r;
    a[id][1].l=l;a[id][1].r=r;

    a[id][0].setv=-1;a[id][1].setv=-1;

    a[id][0].sum=a[id][0].suf=a[id][0].pre=r-l+1;
    a[id][1].sum=a[id][1].suf=a[id][1].pre=r-l+1;

    if(l==r) return ;
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
}
void pushdown(int id,int option){
    if(a[id][option].setv!=-1)
    {
        if(a[id][option].setv==0){//0代表该区间已经是非空闲的阶段,就是已经被预订了时间
            a[lson][option].pre=a[lson][option].sum=a[lson][option].suf=0;
            a[rson][option].pre=a[rson][option].sum=a[rson][option].suf=0;
            a[lson][option].setv=a[rson][option].setv=0;
            a[id][option].setv=-1;
        }else{//说明该区间已经被初始化
            int len=a[id][option].r-a[id][option].l+1;
            a[lson][option].pre=a[lson][option].sum=a[lson][option].suf=len-len/2;;
            a[rson][option].pre=a[rson][option].sum=a[rson][option].suf=len/2;
            a[lson][option].setv=a[rson][option].setv=1;
            a[id][option].setv=-1;
        }
    }
}
void pushup(int id,int option){//区间前后缀的修改要注意
    //更新前缀与后缀
    a[id][option].pre=a[lson][option].pre;//父亲区间的前缀就是左孩子的前缀
    a[id][option].suf=a[rson][option].suf;//父亲区间的后缀就是右孩子的后缀
    //再次更新一下,看是否能将这两个子区间相连接成一个连续的区间,这样就可以使父亲区间的后缀与前缀的长度再一次变长
    if(a[rson][option].suf==a[rson][option].r-a[rson][option].l+1)
          a[id][option].suf+=a[lson][option].suf;
    if(a[lson][option].pre==a[lson][option].r-a[lson][option].l+1)
          a[id][option].pre+=a[rson][option].pre;
    //最大连续区间长度肯定就是下面这几个里面的最大值
    a[id][option].sum=max(a[lson][option].sum,a[rson][option].sum);
    a[id][option].sum=max(a[lson][option].suf+a[rson][option].pre,a[id][option].sum);
}
void update(int id,int l,int r,int val,int op){
    if(a[id][op].l==l&&a[id][op].r==r){
        a[id][op].setv=val;
        if(val) a[id][op].pre=a[id][op].suf=a[id][op].sum=r-l+1;
        else a[id][op].pre=a[id][op].suf=a[id][op].sum=0;
        return ;
    }
//    if(a[id][op].l==a[id][op].r) return ;//当前查询没有意义,并且已经到达底层,那么会经入pushdown导致越界
    pushdown(id,op);
    int mid=(a[id][op].l+a[id][op].r)>>1;
    if(r<=mid) update(lson,l,r,val,op);
    else if(l>mid) update(rson,l,r,val,op);
    else{
        update(lson,l,mid,val,op);
        update(rson,mid+1,r,val,op);
    }
    pushup(id,op);
}
int query(int id,int op,int len)
{
    if(a[id][op].l==a[id][op].r)
        return a[id][op].l;
    pushdown(id,op);

    int mid=(a[id][op].l+a[id][op].r)>>1;
    if(a[lson][op].sum>=len) return query(lson,op,len);//左子树有时间,但不知是哪一个端点因此需要进一步寻找,下面的右子树也是这样
    else if(a[lson][op].suf+a[rson][op].pre>=len) //左子树+右子树有时间,那么区间左端点就可以直接求出
        return mid-a[lson][op].suf+1;
    else return query(rson,op,len);//右子树有时间
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    int t;
    scanf("%d",&t);
    rep(o,1,t){
    printf("Case %d:\n",o);
    scanf("%d%d\n",&n,&m);
    build(1,1,n);
    char str[10];int x;
    rep(i,1,m){
        scanf("%s%d",str,&x);
        if(str[0]=='D'){
            if(a[1][0].sum<x) printf("fly with yourself\n");
            else{
            int p=query(1,0,x);
                printf("%d,let's fly\n",p);
                update(1,p,p+x-1,0,0);
            }
        }else if(str[0]=='N'){
        if(a[1][0].sum>=x){
            int p=query(1,0,x);
            printf("%d,don't put my gezi\n",p);
            update(1,p,p+x-1,0,1);
            update(1,p,p+x-1,0,0);
        }
        else{
            if(a[1][1].sum>=x){
            int p=query(1,1,x);
            printf("%d,don't put my gezi\n",p);
            update(1,p,p+x-1,0,1);
            update(1,p,p+x-1,0,0);
            }else printf("wait for me\n");
        }
        }else{int y;
        scanf("%d",&y);//当需要学习的时候需要将将两者的区间全部修改
            printf("I am the hope of chinese chengxuyuan!!\n");
            update(1,x,y,1,1);
            update(1,x,y,1,0);
        }
    }
    }
    return 0;
}

对于区间最大和问题还有HDU1540

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值