Codeforces Round #437 (Div. 2, based on MemSQL Start[c]UP 3.0 - Round 2) C.Ordering Pizza

C. Ordering Pizza

Problem Statement

    It’s another Start[c]up finals, and that means there is pizza to order for the onsite contestants. There are only 2 types of pizza (obviously not, but let’s just pretend for the sake of the problem), and all pizzas contain exactly S slices.
    It is known that the i-th contestant will eat si slices of pizza, and gain ai happiness for each slice of type 1 pizza they eat, and bi happiness for each slice of type 2 pizza they eat. We can order any number of type 1 and type 2 pizzas, but we want to buy the minimum possible number of pizzas for all of the contestants to be able to eat their required number of slices. Given that restriction, what is the maximum possible total happiness that can be achieved?

Input

    The first line of input will contain integers N and S (1 ≤ N ≤ 105, 1 ≤ S ≤ 105), the number of contestants and the number of slices per pizza, respectively. N lines follow.
    The i-th such line contains integers si, ai, and bi (1 ≤ si ≤ 105, 1 ≤ ai ≤ 105, 1 ≤ bi ≤ 105), the number of slices the i-th contestant will eat, the happiness they will gain from each type 1 slice they eat, and the happiness they will gain from each type 2 slice they eat, respectively.

Output

    Print the maximum total happiness that can be achieved.

Examples

Example 1
input
3 12
3 5 7
4 6 7
5 9 5
output
84
Example 2
input
6 10
7 4 7
5 8 8
12 5 8
6 11 6
3 3 7
5 9 6
output
314

Note

    In the first example, you only need to buy one pizza. If you buy a type 1 pizza, the total happiness will be 3·5 + 4·6 + 5·9 = 84, and if you buy a type 2 pizza, the total happiness will be 3·7 + 4·7 + 5·5 = 74.

题意

有n个人和2种pizza,每份pizza有m片,第i个人想吃 pi 片,他吃一片第一种的pizza,得到的愉悦值为 ai ,而吃一片第二种的pizza,得到的愉悦值为 bi 。问你如何点最少pizza可以让他们的愉悦值总和最大。

思路

首先他要做到的pizza的份数最小,那必定是所有的p值之和除以m的值向上取整。我们可以想到,对于所有人的a值和b值之差进行排序,差是正的尽量给他第一种pizza,是负的尽量给第二种,等于0的话随意。排完序后我们就可以贪心了。对于排序后排在前面的,a-b为正数的人,先尽量点整数份第一种pizza给他们,排在最后的也尽量点整数份第二种pizza。之后中间一部分,我们可以进行枚举,枚举他们吃的是第一种还是第二种,之后值取个max就行了。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
inline void readInt(int &x) {
    x=0;int f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    x*=f;
}
inline void readLong(ll &x) {
    x=0;int f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    x*=f;
}
/*================Header Template==============*/
int n,m,num=0;
struct pizza{
    int s,a,b,diff;
}p[100010];
inline bool cmp(pizza a,pizza b){return a.diff==b.diff?a.a>b.a:a.diff>b.diff;}
main() {
    readInt(n);
    readInt(m);
    for(int i=1;i<=n;i++) {
        readInt(p[i].s);
        readInt(p[i].a);
        readInt(p[i].b);
        p[i].diff=p[i].a-p[i].b;
        num+=p[i].s;
    }
    sort(p+1,p+n+1,cmp);
    int f=0;
    if(num%m)
        f=1;
    num=num/m+f;
    int pos=1,suma=0,liml,limr;
    while(p[pos].diff>0)
        suma+=p[pos++].s;
    suma=suma/m*m;
    liml=pos;
    pos=n;
    int sumb=0;
    while(p[pos].diff<0)
        sumb+=p[pos--].s;
    limr=pos;
    sumb=sumb/m*m;
    ll ans=0;
    int posa=1;
    while(suma>0) {
        ll now=min(p[posa].s,suma);
        ans+=now*p[posa].a;
        p[posa].s-=now;
        suma-=now;
        if(p[posa].s==0)
            posa++;
    }
    int posb=n;
    while(sumb>0&&posb>=posa) {
        ll now=min(p[posb].s,sumb);
        ans+=now*p[posb].b;
        p[posb].s-=now;
        sumb-=now;
        if(p[posb].s==0)
            posb--;
    }
//  cout<<posa<<" "<<posb<<" "<<ans<<" "<<suma<<" "<<sumb<<endl;
    int leftL=0,leftR=0,leftM=0,left=0;
    for(int i=posa;i<=posb;i++)
        if(p[i].a>p[i].b)
            leftL+=p[i].s;
        else if(p[i].a==p[i].b)
            leftM+=p[i].s;
        else
            leftR+=p[i].s;
//  cout<<leftL<<" "<<leftR<<" "<<leftM<<endl;
    left=leftL+leftR+leftM;
    if(left<=m) {
        ll mx=-1,sum=0;
        for(int i=posa;i<=posb;i++)
            sum+=p[i].s*p[i].a;
        mx=max(mx,sum);
        sum=0;
        for(int i=posa;i<=posb;i++)
            sum+=p[i].s*p[i].b;
        mx=max(mx,sum);
        ans+=mx;
    }
    else {
        for(int i=posa;i<=posb;i++)
            if(p[i].a>=p[i].b)
                ans+=p[i].s*p[i].a;
            else
                ans+=p[i].s*p[i].b;
        if((leftR%m==0)||(leftL%m==0)||((leftL+leftM)%m==0)||((leftR+leftM)%m==0));
        else {
            int now=leftL+leftM,tmp=now;
            now=(now/m+1)*m-leftL-leftM;
            int posR=liml; 
            ll sl=0,sr=0;
            while(now>0&&posR<=posb) {
                int x=min(p[posR].s,now);
                sl+=x*(p[posR].b-p[posR].a);
                now-=now;
                if(p[posR].s==x)
                    posR++;
            }
            now=tmp;
            now=(now/m+1)*m-leftR-leftM;
            int posL=limr;
            while(now>0&&posL>=posa) {
                int x=min(p[posL].s,now);
                sl+=x*(p[posL].a-p[posL].b);
                now-=now;
                if(p[posL].s==x)
                    posL--;
            }
            ans-=min(sl,sr);
        } 
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值