P3045 题解

小清新数据结构题。

令已经确定被购买的奶牛的集合为 \(S\)

注意到必然存在一个最优解使得集合 \(S\)\(P_i-C_i\)\(k\) 大的奶牛使用了优惠券。

证明:

令使用优惠券的集合为 \(S'\),显然有 \(|S'|=k\),则花费为:

\[\sum_{i\in S\wedge i\notin S'}P_i+\sum_{i\in S'}C_i \\ =\sum_{i\in S}P_i-\sum_{i\in S'}P_i+\sum_{i\in S'}C_i \\ =\sum_{i\in S}P_i-\sum_{i\in S'}(P_i-C_i) \\ \]

其中 \(\sum_{i\in S}P_i\) 为定值。

不难发现 \(\sum_{i\in S'}(P_i-C_i)\) 需要取到最大值,即 \(S\)\(P_i-C_i\)\(k\) 大的在 \(S'\) 中。

证毕。

\(S=\left\{1,2,\cdots,n\right\}\),试调整 \(S\) 使得其最小花费小于等于 \(m\)。考虑从 \(S\) 中删除一个元素 \(x\) 对于最小花费的贡献:

  1. \(x\notin S'\),则最小花费减小 \(P_x\)
  2. \(x\in S'\),此时 \(x\) 被删除之后,满足 \(P_i-C_i\) 最大的 \(i(i\notin S')\) 被并入 \(S'\),则最小花费减小 \(C_x+P_i-C_i\)

则要使 \(|S|\) 最大,则就要使每次选取对最小花费减小的贡献最大的 \(x\) 删除,即删除 \(\max_{x\notin S'}P_x\)\(\max_{x\in S'}C_x\max_{i\notin S'}(P_i-C_i)\),直到最小花费小于等于 \(m\)

至于具体实现的话,可以考虑用 \(3\) 个优先队列来维护。

#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
#define MAXN 50005
using namespace std;
struct node0{
    int p, c, id;
    bool friend operator<(node0 a, node0 b){
        return a.p - a.c < b.p - b.c;
    }
}cow[MAXN];
struct node1{
    int c, id;
    bool friend operator<(node1 a, node1 b){
        return a.c < b.c;
    }
};
struct node2{
    int p, c, id;
    bool friend operator<(node2 a, node2 b){
        return a.p < b.p;
    }
};
int n, k, ans;
long long m, cost;
bool vis[MAXN];
priority_queue <node0, vector <node0>, less <node0>> q0;
priority_queue <node1, vector <node1>, less <node1>> q1;
priority_queue <node2, vector <node2>, less <node2>> q2;
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
long long _read(){
    long long t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
bool cmp(node0 a, node0 b){
    return a.p - a.c > b.p - b.c;
}
int main(){
    n = read();k = read();m = _read();
    for(int i = 1 ; i <= n ; i ++)
        cow[i].p = read(),cow[i].c = read();
    for(int i = 1 ; i <= n ; i ++)cow[i].id = i;
    ans = n;cost = 0;sort(cow + 1, cow + n + 1, cmp);
    for(int i = 1 ; i <= k && i <= n ; i ++){
        q1.push((node1){cow[i].c, cow[i].id});
        cost += (long long)cow[i].c;
    }
    for(int i = k + 1 ; i <= n ; i ++){
        q0.push((node0){cow[i].p, cow[i].c, cow[i].id});
        q2.push((node2){cow[i].p, cow[i].c, cow[i].id});
        cost += (long long)cow[i].p;
    }
    while(cost > m){
        while(!q0.empty() && vis[q0.top().id] == true)q0.pop();
        while(!q2.empty() && vis[q2.top().id] == true)q2.pop();
        if(q0.empty() || q2.empty()){
            node1 max = q1.top();q1.pop();
            cost -= (long long)max.c;ans--;
        }else{
            node0 dmax = q0.top();
            node1 cmax = q1.top();
            node2 pmax = q2.top();
            if(pmax.p > cmax.c + dmax.p - dmax.c){
                cost -= (long long)pmax.p;ans--;
                vis[pmax.id] = true;q2.pop();
            }else{
                cost -= (long long)(cmax.c + dmax.p - dmax.c);ans--;
                vis[dmax.id] = true;q0.pop();q1.pop();
                q1.push((node1){dmax.c, dmax.id});
            }
        }
    }
    cout << ans << endl;return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值