约会安排(线段树)

题目链接:约会安排

大致题意

这个题… 全中文, 具体的题意麻烦大家看看题面吧.

解题思路

线段树的区间修改和区间查询.

线段树内需要维护当前区间内最大连续长度. 由于女神和基友的查询是有区别的, 所以要分别记录基友和女神的两种情况.

分析题目需要的操作:

​ ①基友和自己约会, 那么需要查看当前区间是否有. 如果有则进行修改.
​ ②女生和自己约会, 先在基友树中寻找是否有时间, 如果没有再在女神树中查找. 若找到则进行区间修改.
​ ③自己想好好学习了, 则清空基友树和女神树的[l, r]区间

对于操作①和②, 我们应到找到满足要求的区间的左端点L, 然后修改[L, L + x - 1]即可.

由于这个题需要区别对待基友和女神, 我们可以考虑开两颗线段树来实现所有操作, 也可以考虑在一个线段树中维护两类信息. 思路都是差不多的.

特别注意的是, 对于女神树的所有操作, 也都会对基友树有同样的操作.

AC代码

/* 一颗线段树维护两类信息 */  
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
struct node {
    int l, r;
    int fmax[2], lmax[2], rmax[2]; //0为基友  1为女神
    int lazy[2];
}t[N << 2];
void pushdown(int id, node& op, int lazy) { //id用于区分基友和女神
    op.fmax[0] = (op.r - op.l + 1) * lazy;
    op.lmax[0] = op.rmax[0] = op.fmax[0];
    op.lazy[0] = lazy;
    
    if (id == 1) {
        op.fmax[1] = op.lmax[1] = op.rmax[1] = op.fmax[0];
        op.lazy[1] = op.lazy[0];
    }
}
void pushdown(int x) {
    if (t[x].lazy[1] != -1) pushdown(1, t[x << 1], t[x].lazy[1]), pushdown(1, t[x << 1 | 1], t[x].lazy[1]);
    if (t[x].lazy[0] != -1) pushdown(0, t[x << 1], t[x].lazy[0]), pushdown(0, t[x << 1 | 1], t[x].lazy[0]);
    t[x].lazy[0] = t[x].lazy[1] = -1;
}

void pushup(int x) {
    node& p = t[x], &l = t[x << 1], &r = t[x << 1 | 1];
    for (int i = 0; i < 2; ++i) {
        p.fmax[i] = max(max(l.fmax[i], r.fmax[i]), l.rmax[i] + r.lmax[i]);
        p.lmax[i] = l.lmax[i] + (l.lmax[i] == l.r - l.l + 1 ? r.lmax[i] : 0);
        p.rmax[i] = r.rmax[i] + (r.rmax[i] == r.r - r.l + 1 ? l.rmax[i] : 0);
    }
}

void build(int l, int r, int x = 1) {
    t[x] = { l, r, 1, 1, 1, 1, 1, 1, -1, -1 };
    if (l == r) return;
    int mid = l + r >> 1;
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
    pushup(x);
}

void modify(int l, int r, int id, int c, int x = 1) {
    if (l <= t[x].l && r >= t[x].r) {
        pushdown(id, t[x], c);
        return;
    }
    pushdown(x);
    int mid = t[x].l + t[x].r >> 1;
    if (l <= mid) modify(l, r, id, c, x << 1);
    if (r > mid) modify(l, r, id, c, x << 1 | 1);
    pushup(x);
}

int findleft(int l, int r, int id, int c, int x = 1) { //返回至少有c连续空闲时间的区间左端点
    if (t[x].l == t[x].r) return t[x].l;
    pushdown(x);
    if (t[x << 1].fmax[id] >= c) return findleft(l, r, id, c, x << 1);
    if (t[x << 1].rmax[id] + t[x << 1 | 1].lmax[id] >= c) return t[x << 1].r - t[x << 1].rmax[id] + 1;
    return findleft(l, r, id, c, x << 1 | 1);
}
int main()
{
    int T; cin >> T;
        rep(CASE, T) {
            printf("Case %d:\n", CASE);
            int n, m; scanf("%d %d", &n, &m);
            build(1, n);
            while (m--) {
                char s[10]; scanf("%s", s);
                if (*s == 'S') {
                    int l, r; scanf("%d %d", &l, &r);
                    modify(l, r, 1, 1);
                    printf("I am the hope of chinese chengxuyuan!!\n");
                }
                else {
                    int c; scanf("%d", &c);
                    
                    if (*s == 'N') {
                        int id = -1;
                        if (t[1].fmax[0] >= c) id = 0;
                        else if (t[1].fmax[1] >= c) id = 1;
                        
                        if (id != -1) {
                            int l = findleft(1, n, id, c);
                            modify(l, l + c - 1, 1, 0);
                            printf("%d,don't put my gezi\n", l);
                        }
                        else printf("wait for me\n");
                    }
                    else if (*s == 'D') {
                        if (t[1].fmax[0] >= c) {
                            int l = findleft(1, n, 0, c);
                            modify(l, l + c - 1, 0, 0);
                            printf("%d,let's fly\n", l);
                        }
                        else printf("fly with yourself\n");
                    }
                }
            }
        }
    return 0;
}


/* 两颗线段树维护方式 */
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
struct node {
    int l, r;
    int fmax, lmax, rmax;
    int lazy;
}t1[N << 2], t2[N << 2]; // 基友, 女神
void pushdown(node& op, int lazy) {
    op.fmax = lazy * (op.r - op.l + 1);
    op.lmax = op.fmax, op.rmax = op.fmax;
    op.lazy = lazy;
}
void pushdown(node t[], int x) {
    if (t[x].lazy == -1) return;
    pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
    t[x].lazy = -1;
}

void pushup(node& p, node& l, node& r) {
    p.fmax = max(max(l.fmax, r.fmax), l.rmax + r.lmax);
    p.lmax = l.lmax + (l.lmax == l.r - l.l + 1 ? r.lmax : 0);
    p.rmax = r.rmax + (r.rmax == r.r - r.l + 1 ? l.rmax : 0);
}
void pushup(node t[], int x) { pushup(t[x], t[x << 1], t[x << 1 | 1]); }

void build(int l, int r, int x = 1) {
    t1[x] = t2[x] = { l, r, 1, 1, 1, -1 };
    if (l == r) return;
    int mid = l + r >> 1;
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
    pushup(t1, x), pushup(t2, x);
}

int findleft(node t[], int c, int x = 1) { //找到当前树中最左侧有连续c空闲时间的左端点(起始点)
    if (t[x].l == t[x].r) return t[x].l;
    pushdown(t, x);
    if (t[x << 1].fmax >= c) return findleft(t, c, x << 1);
    if (t[x << 1].rmax + t[x << 1 | 1].lmax >= c) return t[x << 1].r - t[x << 1].rmax + 1;
    return findleft(t, c, x << 1 | 1);
}

void modify(node t[], int l, int r, int x = 1) { //修改某一课树的
    if (l <= t[x].l && r >= t[x].r) { pushdown(t[x], 0); return; }
    pushdown(t, x);
    int mid = t[x].l + t[x].r >> 1;
    if (l <= mid) modify(t, l, r, x << 1);
    if (r > mid) modify(t, l, r, x << 1 | 1);
    pushup(t, x);
}
void modify(int l, int r, int c, int x = 1) { //两棵树一起修改的
    if (l <= t1[x].l && r >= t1[x].r) {
        pushdown(t1[x], c), pushdown(t2[x], c);
        return;
    }
    pushdown(t1, x), pushdown(t2, x);
    int mid = t1[x].l + t1[x].r >> 1;
    if (l <= mid) modify(l, r, c, x << 1);
    if (r > mid) modify(l, r, c, x << 1 | 1);
    pushup(t1, x), pushup(t2, x);
}
int main()
{
    int T; cin >> T;
    rep(Case, T) {
        printf("Case %d:\n", Case);
        int n, m; scanf("%d %d", &n, &m);
        build(1, n);
        while (m--) {
            char s[10]; scanf("%s", s);
            if (*s == 'S') {
                int l, r; scanf("%d %d", &l, &r);
                modify(l, r, 1);
                printf("I am the hope of chinese chengxuyuan!!\n");
            }
            else {
                int x; scanf("%d", &x);
                if (*s == 'N') {
                    if (t1[1].fmax >= x) { //基友树有足够的时间
                        int l = findleft(t1, x);
                        modify(l, l + x - 1, 0);
                        printf("%d,don't put my gezi\n", l);
                    }
                    else if (t2[1].fmax >= x){ //女神树有足够的时间
                        int l = findleft(t2, x);
                        modify(l, l + x - 1, 0);
                        printf("%d,don't put my gezi\n", l);
                    }
                    else printf("wait for me\n");
                }
                else if (*s == 'D') {
                    if (t1[1].fmax >= x) {
                        int l = findleft(t1, x);
                        modify(t1, l, l + x - 1);
                        printf("%d,let's fly\n", l);
                    }
                    else printf("fly with yourself\n");
                }
            }
        }
    }
    return 0;
}

这个题要注意题目中的字符, 都需要是英文字符, 如果你怕粘贴错误, 不妨直接粘贴本文代码的输出语句.

(据听说原题中是有中文的字符的, 恐怖如斯QAQ)

kuangbin线段树专题点这里!!!

END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值