HDU - 4553 约会安排 (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4553

题目思路:本题就是一个线段树的区间合并,定义三个懒惰标记:屌丝标记,女神标记,学习标记,三个的优先级为学习 > 女神 > 屌丝,更新时按照这个优先级更新即可,查询时,屌丝就直接查看屌丝区间是否有空闲时间,女神优先看屌丝区间和女神区间是否有空闲时间,否则就覆盖已有的屌丝区间,学习就是将屌丝区间和女神区间内的所有值清空,具体操作代码里都有解释。

AC代码如下:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define FIN freopen("in.txt","r",stdin)
#define fuck(x) cout<<'['<<x<<']'<<endl
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int>pii;
const int MX = 1e5 + 10;

struct node {
    int n, d, s;//n代表女神的懒惰标记,d代表屌丝的懒惰标记,s代表学习的懒惰标记;
    int nls, nrs, nms;//分别代表女神的左连续最长区间,右连续最长区间和总连续最长区间;
    int dls, drs, dms;//分别代表屌丝的左连续最长区间,右连续最长区间和总连续最长区间;
} a[MX << 2];//所储存的是可用的空闲时间是多少,方便查询;

void update_d(int rt) {
    a[rt].d = 1;
    a[rt].dls = a[rt].drs = a[rt].dms = 0;
}

void update_n(int rt) {
    a[rt].n = 1;
    a[rt].d = 0;
    a[rt].nls = a[rt].nrs = a[rt].nms = 0;
    a[rt].dls = a[rt].drs = a[rt].dms = 0;//女神的优先级高于屌丝,所以更新女神时也要把屌丝的区间长度也更新;
}

void update_s(int l, int r, int rt) {
    a[rt].s = 1;
    a[rt].d = a[rt].n = 0;
    a[rt].dls = a[rt].drs = a[rt].dms = r - l + 1;
    a[rt].nls = a[rt].nrs = a[rt].nms = r - l + 1;
    //学习的优先级最高,所以更新时需将整个区间内女神和屌丝的所有标记都更新掉;
}

void Push_Up(int l, int r, int rt) {
    int m = (l + r) >> 1;
    a[rt].dms = max(a[rt << 1].dms, max(a[rt << 1 | 1].dms, a[rt << 1].drs + a[rt << 1 | 1].dls));
    //父节点的总连续区间长度 = MAX(左儿子的左连续最长区间,右儿子的右连续最长区间,左儿子的右连续区间 + 右儿子的左连续最长区间);
    a[rt].dls = a[rt << 1].dls;
    a[rt].drs = a[rt << 1 | 1].drs;
    if(a[rt].dls == m - l + 1)//如果左儿子的左连续最长区间是满的,那么父节点的左连续最长区间要加上右儿子的左连续最长区间;
        a[rt].dls += a[rt << 1 | 1].dls;
    if(a[rt].drs == r - m)//右儿子同理;
        a[rt].drs += a[rt << 1].drs;

    a[rt].nms = max(a[rt << 1].nms, max(a[rt << 1 | 1].nms, a[rt << 1].nrs + a[rt << 1 | 1].nls));
    a[rt].nls = a[rt << 1].nls;
    a[rt].nrs = a[rt << 1 | 1].nrs;
    if(a[rt].nls == m - l + 1)
        a[rt].nls += a[rt << 1 | 1].nls;
    if(a[rt].nrs == r - m)
        a[rt].nrs += a[rt << 1].nrs;
}

void Push_Down(int l, int r, int rt) {
    int m = (l + r) >> 1;
    if(a[rt].s) {
        update_s(lson);
        update_s(rson);
        a[rt].s = 0;
    }//更新时学习的优先级最高,优先更新;
    if(a[rt].n) {
        update_n(rt << 1);
        update_n(rt << 1 | 1);
        a[rt].n = 0;
    }//女神优先级第二,接着更新女神;
    if(a[rt].d) {
        update_d(rt << 1);
        update_d(rt << 1 | 1);
        a[rt].d = 0;
    }//最后更新屌丝;
}

void study(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        update_s(l, r, rt);
        return;
    }
    int m = (l + r) >> 1;
    Push_Down(l, r, rt);
    if(L <= m) study(L,R,lson);
    if(R > m) study(L,R,rson);
    Push_Up(l, r, rt);
}

void Update(int L, int R, int c, int l, int r, int rt) {
    if(L <= l && r <= R) {
        if(c == 0)//0代表屌丝;
            update_d(rt);
        else
            update_n(rt);
        return;
    }
    int m = (l + r) >> 1;
    Push_Down(l, r, rt);
    if(L <= m) Update(L,R,c,lson);
    if(R > m) Update(L,R,c,rson);
    Push_Up(l, r, rt);
}

int Query(int w, int f, int l, int r, int rt) {
    if(l == r) return l;
    Push_Down(l, r, rt);
    int m = (l + r) >> 1;
    if(f == 0) {
        if(a[rt << 1].dms >= w)
            return Query(w, f, lson);//如果左儿子的总连续区间大于所要的长度,则往左儿子更新;
        else if(a[rt << 1].drs + a[rt << 1 | 1].dls >= w)
            return m - a[rt << 1].drs + 1;//如果是合并后的区间大于所要长度,则直接返回;
        else
            return Query(w, f, rson);//如果右儿子的总连续区间大于所要的长度,则往右儿子更新;
    } else {//女神的查询同理;
        if(a[rt << 1].nms >= w)
            return Query(w, f, lson);
        else if(a[rt << 1].nrs + a[rt << 1 | 1].nls >= w)
            return m - a[rt << 1].nrs + 1;
        else
            return Query(w, f, rson);
    }
}

int T;
int n, m;

int main() {
    // FIN;
    scanf("%d", &T);
    int cas = 1;
    while(T--) {
        scanf("%d%d", &n, &m);
        study(1, n, 1, n, 1);
        printf("Case %d:\n", cas++);
        while(m--) {
            char op[10];
            int x, y;
            scanf("%s", op);
            if(op[0] == 'D') {
                scanf("%d", &x);
                if(a[1].dms < x)//如果可用的时间不够,直接输出;
                    puts("fly with yourself");
                else {
                    int ans = Query(x, 0, 1, n, 1);
                    Update(ans, ans + x - 1, 0, 1, n, 1);
                    printf("%d,let's fly\n", ans);
                }
            } else if(op[0] == 'N') {
                scanf("%d", &x);
                if(a[1].dms < x) {//如果屌丝区间剩余的时间不够,则查看女神区间;
                    if(a[1].nms < x)
                        puts("wait for me");
                    else {
                        int ans = Query(x, 1, 1, n, 1);
                        Update(ans, ans + x - 1, 1, 1, n, 1);
                        printf("%d,don't put my gezi\n", ans);
                    }
                }
                else{//否则直接占用屌丝区间的时间;
                    int ans = Query(x,0,1,n,1);
                    Update(ans,ans + x - 1,1,1,n,1);
                    printf("%d,don't put my gezi\n", ans);
                }
            } else {
                scanf("%d%d", &x, &y);
                study(x, y, 1, n, 1);
                puts("I am the hope of chinese chengxuyuan!!");
            }
        }
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值