NC 17315 二分 + 优先队列

题意

传送门 NC 17315

题解

求满足 ∑ 0 ≤ j < m b j ≤ v \sum\limits_{0\leq j<m}b_{j}\leq v 0j<mbjv 时物品价值的最大中位数。先讨论物品数量为奇数的情况,为了满足约束条件,使物品按价值有序,对于答案对应的物品,在背包中大于等于以及小于等于它的物品数量都至少为 m / 2 m/2 m/2;因为中位数对应的物品体积一定要计入背包,那么目标是使大于等于以及小于等于它的 m / 2 m/2 m/2 个物品总体积最小。分别向前以及向后遍历一遍,用优先队列维护物品左侧与右侧数量为 m / 2 m/2 m/2 的物品体积和的最小值。最后枚举物品,满足条件且价值最大的即答案。

物品数量为偶数的情况复杂一些,若枚举任意一对节点,复杂度达到 O ( n 2 ) O(n^2) O(n2),显然是不行的。观察到按价值单调不减的物品,其右侧(或左侧)的某个固定数量的体积和单调不增,那么可以枚举中间两个物品的其中一个,二分求另外一个。

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
struct node
{
    int a, b, ls, rs;
    bool operator<(const node &nd) const
    {
        return a < nd.a;
    }
} ns[maxn];
int v, n, m;

int main()
{
    scanf("%d%d%d", &v, &n, &m);
    for (int i = 0; i < n; i++)
    {
        scanf("%d%d", &ns[i].a, &ns[i].b);
    }
    sort(ns, ns + n);
    priority_queue<int> q;
    int f = m & 1, limit = m / 2, sum = 0;
    for (int i = 0; i < n; i++)
    {
        int v = ns[i].b;
        q.push(v);
        ns[i].ls = sum, sum += v;
        if (q.size() > limit - 1 + f)
        {
            sum -= q.top();
            q.pop();
        }
    }
    while (!q.empty())
        q.pop();
    sum = 0;
    for (int i = n - 1; i > 0; i--)
    {
        int v = ns[i].b;
        q.push(v);
        ns[i].rs = sum, sum += v;
        if (q.size() > limit)
        {
            sum -= q.top();
            q.pop();
        }
    }
    int res = -1;
    if (f)
    {
        for (int i = limit; i < n - limit; i++)
        {
            if (ns[i].b + ns[i].ls + ns[i].rs <= v)
            {
                res = max(res, ns[i].a);
            }
        }
    }
    else
    {
        for (int i = limit - 1; i < n - limit; i++)
        {
            int lb = i, ub = n - limit;
            while (ub - lb > 1)
            {
                int mid = (lb + ub) >> 1;
                if (ns[i].ls + ns[i].b + ns[mid].rs <= v)
                    lb = mid;
                else
                    ub = mid;
            }
            if (ns[i].ls + ns[i].b + ns[ub - 1].rs <= v)
                res = max(res, ns[i].a + ns[ub].a);
        }
    }
    printf("%d\n", f ? res : res / 2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值