题解 Gym 104531D【Coffee】

发布时间 2023-09-11 17:56:08作者: caijianhong

2022 SYSU School Contest

题目不想翻译了,自己看能看懂。

problam

The girls of HTT like drinking tea. But one day, they wanted a change and decided to try coffee in the next \(n\) days.

Now Mugi, who always provides food and drinks for HTT, will go to the shop to buy some coffee. She learns that the price of coffee on day \(i\) is \(a_i\) . Now she has \(m\) coupons. The \(i\) th coupon can be used before day \(r_i\) (it can also be used on day \(r_i\)) and can reduce the price of coffee on that day by \(w_i\) . \(\textbf{Notice that the price can be a negative number after using the coupon.}\) Each coupon can be used only once, and Mugi cannot use more than one coupon per day.

Since the girls of HTT still need to drink tea, Mugi decided to choose \(\textbf{exactly}\) \(k\) days to buy coffee. Now she wants to know the minimum money she will spend (or the maximum money she can get) to choose exactly \(k\) days to buy coffee. \(n,m\leq 10^5,a_i\leq 10^9\)

solution

首先可以搞出一个费用流模型,比较显然,所以我们猜测答案关于 \(k\) 是下凸的。所以 wqs 二分,将每杯咖啡的价格 \(-mid\) 之后,要求买任意多的咖啡,还要最小费用,那么当然是只买负数的咖啡。

考虑对着时间扫描一下,对于一张优惠 \(r\) 的优惠券,对它前面的咖啡,可以这样使用:

  • 如果一个咖啡 \(a\) 还没被买,贪心买了它,\(ans:=ans+a-r\)
  • 如果一个咖啡已经被买了,之前用的优惠券是 \(r_2\),但是我现在比 \(r_2\) 的优惠力度更大,那么我可以换:\(ans:=ans+r_2-r\)

想到将咖啡和优惠券放到一个小根堆里,每次用优惠券检查堆顶,对答案的贡献是否 \(\leq 0\),如果是,用了,向上面那样统计答案,同时为了第二种情况将这张优惠券入堆(将优惠券当成咖啡来卖??!)。这样加上最终还在堆里的负数咖啡,能算出最小的花费。

然后是细节,wqs 二分的凸包有三点共线的情况,不妨直接钦定切三点共线取最右边的点,那么我们要尽可能多的买咖啡,咖啡和优惠券价值相同时优先拿走咖啡。另外 wqs 二分的边界是答案的差分(无法估计用答案的值域也行?),这一题明显是 \(a_i\)\([-10^9,10^9]\)

code

点击查看代码
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<class T> using pqueue=priority_queue<T,vector<T>,greater<T>>;
int n,m,K;
LL a[1<<17];
vector<LL> buc[1<<17];
pair<int,LL> check(LL k){
    //a := map (\x -> x - k) a
    debug("k=%lld\n",k);
    int cnt=0; LL sum=0;
    pqueue<pair<LL,int>> q;
    for(int i=1;i<=n;i++){
        q.push({a[i]-k,-1});
        for(LL r:buc[i]){
            if(!q.empty()&&q.top().first<=r){
                auto t=q.top(); q.pop();
                cnt+=t.second,sum+=t.first-r,q.push({r,0});
            }
        }
    }
    while(!q.empty()&&q.top().first<=0){
        auto t=q.top(); q.pop();
        cnt+=t.second,sum+=t.first;
    }
    debug("cnt=%d,sum=%lld\n",cnt,sum);
    return {-cnt,sum};
}
LL binary(LL l,LL r){
    LL ans=1e18;
    for(LL mid=(l+r)>>1;l<=r;mid=(l+r)>>1){
        auto res=check(mid);
        if(res.first>=K) ans=res.second+K*mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}
int main(){
//  #ifdef LOCAL
//      freopen("input.in","r",stdin);
//  #endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1,r,x;i<=m;i++) scanf("%d%d",&r,&x),buc[r].push_back(x);
    for(int i=1;i<=n;i++) sort(buc[i].begin(),buc[i].end(),greater<LL>());
    scanf("%d",&K);
    printf("%lld\n",binary(-1e9,1e9));
    return 0;
}