新疆大学生程序设计大赛 H - Magic Transport

链接:新疆大学生程序设计大赛 H - Magic Transport

线段树做法

分析

  • 就是说当前在第 i i i 个的时候,在 [ 1 , i − 1 ] [1, i - 1] [1,i1] 里去找最多的不超过 m − a [ i ] m - a[i] ma[i] 的个数。
  • 暴力的想法,就是每当我们到 i i i 的时候, [ 1 , i − 1 ] [1, i - 1] [1,i1] 都已经按照升序排序好了,那么直接二分就好了。(当然暴力是会寄的)

思路

  • 定义一个二元组 ( a , b ) (a,b) (a,b) a a a 表示重量, b b b 表示排序后的位置。
  • 更新就单点修改 b b b 位置,这个 b b b 就如我们暴力的想法一样相当于是到 i i i 时 , [ 1 , i − 1 ] [1, i - 1] [1,i1] 都已经排好序了。
  • 查询就二分线段树,细节看 c o d e code code
//道可道非常道,名可名非常名

#include <bits/stdc++.h>

using ll = long long;

template<class Info>
struct SegmentTree {
#define ls i << 1
#define rs i << 1 | 1

    const int N;
    std::vector<Info> seg;
    SegmentTree(int _) : N(_), seg((_ + 1) << 4) {}
    SegmentTree(std::vector<int> a) : SegmentTree(int(a.size())) {
        std::function<void(int, int, int)> build = [&](int i, int l, int r) {
            seg[i] = {l, r};
            if (l == r) {
//                seg[i].max = a[i - 1];
                return ;
            }

            int mid = (l + r) / 2;
            build(ls, l, mid);
            build(rs, mid + 1, r);
            pushup(i);
        };
        build(1, 1, N);
    }

    void pushup(int i) {
//        seg[i] = seg[ls] + seg[rs];
        seg[i].sum = seg[ls].sum + seg[rs].sum;
        seg[i].max = seg[ls].max + seg[rs].max;
    }

    void modify(int i, int ind, ll v) {
        if (seg[i].l == seg[i].r) {
            seg[i].max = 1;
            seg[i].sum = v;
            return ;
        }

        if (ind <= seg[ls].r) {
            modify(ls, ind, v);
        } else {
            modify(rs, ind, v);
        }
        pushup(i);
    }

    ll query(int i, int l, int r) {
        if (l <= seg[i].l and seg[i].r <= r) {
            return seg[i].max;
        }

        ll res = -1e18;
        if (l <= seg[ls].r) {
            res = std::max(res, query(ls, l, r));
        }
        if (r >= seg[rs].l) {
            res = std::max(res, query(rs, l, r));
        }
        return res;
    }

    ll Query(int i, ll q) {
        if (seg[i].l == seg[i].r) {
            return q >= seg[i].sum ? seg[i].max : 0ll;
        }

        if (q >= seg[ls].sum) { // 大于等于左区间重量则左区间全满足,则加上左区间数量 并 查询右区间
            return seg[ls].max + Query(rs, q - seg[ls].sum); // 此时 q 应减去左区间重量
        } else {
            return Query(ls, q); // 小于左区间重量则继续递归左区间
        }
    }
};

struct Info {
    int l, r;
    ll sum, max;
};

Info operator + (const Info &u, const Info &v) {
    Info res = {};
    res.l = u.l;
    res.r = v.r;
    res.sum = u.sum + v.sum;
    res.max = std::max(u.max, v.max);
    return res;
}

void solve() {
    int n, m;
    std::cin >> n >> m;

    std::vector<std::array<int, 2>> ord(n + 1);
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
        ord[i][0] = a[i];
        ord[i][1] = i;
    }

    std::sort(ord.begin() + 1, ord.end());
    std::map<std::array<int, 2>, int> map;
    for (int i = 1; i <= n; ++i) {
        map.insert({ord[i], i}); // 此时map[ord[i]] 就是文中的 b
    }

    std::vector<int> SEG(n);
    SegmentTree<Info> seg(SEG);

    for (int i = 1; i <= n; ++i) {
        if (i == 1) {
            std::cout << "0 ";
        } else {
            if (m - a[i] >= 0) {
                std::cout << i - 1 - seg.Query(1, m - a[i]) << " ";
            } else {
                std::cout << "0 ";
            }
        }
        seg.modify(1, map[{a[i], i}], a[i]);
    }
    std::cout << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }

    return 0;
}

//
  • 30
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Heredy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值