NOIP2023模拟6联测27 C. 点餐

NOIP2023模拟6联测27 C. 点餐

题目大意

n n n 种菜品,每样菜品有 a i , b i a_i , b_i ai,bi

假设有某位顾客点了 k k k 样菜品,那么价格为 ∑ i = 1 k a p i + max ⁡ i = 1 k b p i \sum_{i = 1}^k a_{p_i}+\max_{i = 1}^kb_{p_i} i=1kapi+maxi=1kbpi

询问所有的 k ∈ ( 1 , n ) k \in(1 , n) k(1,n) 的答案。

思路

先把输入按 b b b 排序

w ( k , x ) w(k ,x) w(k,x) 为要选在前 x x x 里面选 k k k 个,那么

就是前 x x x 个菜品内最小的 k − 1 k - 1 k1 a a a 加上 a x + b x a_x +b_x ax+bx

显然,决策满足单调性,所以可以用一个分治来搞

维护最小的前 k − 1 k - 1 k1 a a a 可以用主席树

code

#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define LL long long
using namespace std;
const int N = 2e5 + 5;
const LL inf = 1e18 + 5;
int n , pos , rt[N] , m , ans1[N];
LL ans[N] , a[N];
struct Re {
    LL a , b;
} re[N << 1];
struct Tr {
    int lp , rp , cnt;
    LL val;
} tr[10000005];
bool cmp1 (Re x , Re y) { return x.a < y.a; }
bool cmp2 (Re x , Re y) { return x.b < y.b; }
void glp (int p) {
    if (!tr[p].lp) 
        tr[p].lp = ++pos;
}
void grp (int p) {
    if (!tr[p].rp) 
        tr[p].rp = ++pos;
}
void change (int lst , int p , int l , int r , int x) {
    if (l == r) {
        tr[p].cnt ++;
        tr[p].val += a[l];
    }
    else {
        int mid = l + r >> 1;
        if (x <= mid) {
            glp (p);
            tr[p].rp = tr[lst].rp;
            change (tr[lst].lp , tr[p].lp , l , mid , x);
        }
        else {
            grp (p);
            tr[p].lp = tr[lst].lp;
            change (tr[lst].rp , tr[p].rp , mid + 1 , r , x);
        }
        tr[p].val = tr[tr[p].lp].val + tr[tr[p].rp].val;
        tr[p].cnt = tr[tr[p].lp].cnt + tr[tr[p].rp].cnt;
    }
}
LL getsum (int p , int l , int r , int k) {
    if (l == r)
        return a[l];
    else {
        int mid = l + r >> 1;
        if (tr[tr[p].lp].cnt >= k) {
            return getsum (tr[p].lp , l , mid , k);
        }
        else {
            return getsum (tr[p].rp , mid + 1 , r , k - tr[tr[p].lp].cnt) + tr[tr[p].lp].val;
        }
    }
}
void solve (int l , int r , int L , int R) {
    if (l > r) 
        return;
    int mid = l + r >> 1 , now1=  0;
    LL now;
    fu (i , max (L , mid) , R) {
        // now = re[i].b + re[i].a + getsum (rt[i - 1] , 1 , n , mid - 1);
        now = re[i].b + getsum (rt[i] , 1 , n , mid);
        if (ans[mid] > now) {
            ans[mid] = now;
            now1 = i;
            ans1[mid] = i;
        }
    }
    solve (l , mid - 1 , L , now1);
    solve (mid + 1 , r , now1 , R);
}
int main () {
    freopen ("order.in" , "r" , stdin);
    freopen ("order.out" ,"w" , stdout);
    scanf ("%d" , &n);
    fu (i , 1 , n)
        scanf ("%lld%lld" , &re[i].a , &re[i].b);
    sort (re + 1 , re + n + 1 , cmp1);
    fu (i , 1 , n) a[i] = re[i].a , re[i].a = i;
    sort (re + 1 , re + n + 1 , cmp2);
    fu (i , 1 , n) ans[i] = inf;
    fu (i , 0 , n) rt[i] = ++pos;
    fu (i , 1 , n) {
        change (rt[i - 1] , rt[i] , 1 , n , re[i].a);
    }
    solve (1 , n , 1 , n);
    fu (i , 1 , n) {
        printf ("%lld\n" , ans[i]);
    }
    return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值