贪心+线段树,CF720A. Closing ceremony

目录

一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

二、解题报告

1、思路分析

2、复杂度

3、代码详解


一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

Problem - 720A - Codeforces


二、解题报告

1、思路分析

如果人都在左上角如何分配?

把人按照耐力排序,位置按照权值排序,从小到大分配,就变成了贪心入门问题中的给小孩分糖果问题

现在变成了两拨人,每个位置对两拨人有不同的权值,如何分配?

先把每拨人升序排序,然后先处理左边这拨人。

顺序遍历左部人,选取当前这个人能拿的位置中对右边那拨人最远的位置,这样有个好处:由于升序排序,所以当前人能拿的位置后面人也能拿,同时又保证了对右部人影响最小

对于位置的维护可以用set或者线段树

set不容易写错,但是为了练习下线段树还是用了线段树,结果调了1h x_x

2、复杂度

时间复杂度: O(mn logmn)空间复杂度:O(mn)

3、代码详解

#include <bits/stdc++.h>
const int N = 1e4 + 10;
#define lc p << 1
#define rc p << 1 | 1
typedef std::pair<int, int> PII;
int n, m, k, l, tot;
int dstk[N], dstl[N];
PII vk[N];
struct node {
    int l, r, vl, id;
} tr[N << 4];
void pushup(int p) {
    if(tr[lc].vl > tr[rc].vl)
        tr[p].vl = tr[lc].vl, tr[p].id = tr[lc].id;
    else 
        tr[p].vl = tr[rc].vl, tr[p].id = tr[rc].id;
}
void build(int p, int l, int r) {
    tr[p] = { l, r, vk[l].second, l };
    if (l == r) return;
    int mid = l + r >> 1;
    build(lc, l, mid), build(rc, mid + 1, r);
    pushup(p);
}
void update(int p, int x, int v) {
    if(tr[p].l == x && tr[p].r == x) {
        tr[p].vl = tr[p].id = v;
        return;
    }
    int mid = tr[p].l + tr[p].r >> 1;
    if(x <= mid) update(lc, x, v);
    else update(rc, x, v);
    pushup(p);
}
PII query(int p, int l, int r) {
    if (l <= tr[p].l && tr[p].r <= r) {
        return std::make_pair(tr[p].id, tr[p].vl);
    }
    int mid = tr[p].l + tr[p].r >> 1;
    PII res = std::make_pair(-1, -1);
    if(l <= mid) {
        PII t = query(lc, l, r);
        if (t.second > res.second) res = t;
    }
    if(r > mid) {
        PII t = query(rc, l, r);
        if (t.second > res.second) res = t;
    }    
    return res;
}

int find(int x) {
    int l = 1, r = tot, res = 1;
    while(l <= r) {
        int mid = l + r >> 1;
        if (vk[mid].first <= x) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    return res;
}

int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    std::cin >> n >> m >> k;
    for (int i = 0; i < k; i ++) std::cin >> dstk[i];
    std::cin >> l;
    for (int i = 0; i < l; i ++) std::cin >> dstl[i];
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            vk[++ tot] = std::make_pair(i + j, i + m + 1 - j);
    std::sort(vk , vk + tot);
    std::sort(dstk, dstk + k), std::sort(dstl, dstl + l);
    build(1, 1, tot);
    for (int i = 0; i < k; i ++) {
        int r = find(dstk[i]);
        PII t = query(1, 1, r);
        if (t.second == -1) {
            std::cout << "NO";
            return 0;
        }
        update(1, t.first, -1);
    }
    std::vector<int> res;
    res.reserve(tot);
    for(int i = 1; i <= tot; i ++) {  
        PII t = query(1, i, i);
        if(~t.second)
            res.push_back(t.second);
    }
    std::sort(res.begin(), res.end());
    for (int i = 0, t = 0; i < l; i ++) {
        if (t > res.size() || dstl[i] < res[t]) {
            std::cout << "NO";
            return 0;
        }
        t ++;
    }
    std::cout << "YES";
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EQUINOX1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值