题解:CF1969D Shop Game

思路

考虑 Bob 的策略:

如果 Alice 选了物品 ii,那么 Bob 如果选了物品 ii 会使得 Alice 对于这个物品亏多少钱。

原来赚的钱:biaib_{i}-a_{i}

Bob 选后本金的亏损:ai-a_{i}

那么 Bob 如果选了物品 ii 会就使得 Alice 对于这个物品亏: (biai)ai=bi-(b_{i}-a_{i})-a_{i}=-b_{i}

所以我们知道 Bob 应该选前 kkbib_{i}ii 才能使在目前 Alice 选出的物品中让他亏损最多。

考虑 Alice 的策略:

首先 Alice 选出的前 kk 大的物品一定不赚钱。

那么如果选的物品不多于 kk 且至少选了一个物品,就一定是亏的,在最坏的情况下,我们可以什么都不选来保证不亏钱。

所以可以选 kk 个物品来献祭,来让其它选的物品赚钱,但也不是选哪个献祭都可以,那 kk 个献祭的物品的 bib_{i} 必须在所有选出的物品的 bb 里是前 kk 大。

那么如何才能让献祭的物品由我们选择呢?可以选择将 bb 由大到小排序,这样最左边的我们选择的 kk 个物品就一定是献祭的,又由于我们需要献祭的钱最少,所以选 aia_{i}kk 小的 ii 就可以了。那么选的第 kk 个物品往后怎么选?只需要挑 bi>aib_{i}>a_{i} 的选就行了,因为如果选其它的必然不赚钱。

但是我们不能只考虑前 kk 个物品献祭,所以要枚举考虑献祭的最后一个 ii 的分界线。

做法

bib_{i} 排序,预处理 iinn 的买入价小于卖入价物品的利润,建立大根堆,存考虑到献祭的前 kk 小的 aia_{i},维护堆内和。接下来枚举考虑献祭的最后一个 ii,将 aia_{i} 入堆,如果堆的大小大于 kk 那么弹出堆顶。左边选前 kkaia_{i}ii 物品,右边选 bi>aib_{i}>a_{i}ii 物品。用右边利润和减去堆内和来更新 ansans 的最大值。

注意 kk00 时要特判,因为后面的做法考虑的是 Bob 至少选一个物品。

ansans 一开始初值为 00,因为 Alice 可以什么都不选。

那么这道题就做完了,代码其实不长。

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
bool cmp(pair<int,int> x,pair<int,int> y)
{
	return x.first>y.first;
}
signed main()
{
    int tt;
    cin>>tt;
    while(tt--)
    {
        int n,k;
        cin>>n>>k;
        vector<pair<int,int> > a(n+1);
		vector<int> pre(n+2);
        for(int i=1;i<=n;i++) cin>>a[i].second;
        for(int i=1;i<=n;i++) cin>>a[i].first;
        sort(a.begin()+1,a.end(),cmp);
		for(int i=n;i>=1;i--) pre[i]=max(a[i].first-a[i].second,(int)0)+pre[i+1];
        priority_queue<int> q;
        int ans=0,sum=0;
        if(k==0)
        {
        	cout<<pre[1]<<endl;
        	continue;
		}
        for(int i=1;i<=n;i++)
        {
            q.push(a[i].second);
            sum+=a[i].second;
            if(q.size()>k&&q.size()) sum-=q.top(),q.pop();
			if(q.size()==k) ans=max(ans,pre[i+1]-sum);
        }
        cout<<ans<<endl;
    }
}
posted @ 2024-05-08 12:56  PM_pro  阅读(26)  评论(0)    收藏  举报  来源