【Codeforces Round #437 (Div. 2) C】 Ordering Pizza

weixin_30588675發表於2020-04-05

【連結】h在這裡寫連結


【題意】


    給你參賽者的數量以及一個整數S表示每塊披薩的片數。
    每個引數者有3個引數,si,ai,bi;
    表示第i個參賽者它要吃的披薩的片數,以及吃一片第一種披薩增加的幸福感,
    以及吃一片第二種披薩增加的幸福感。

    兩種披薩都能任意數量地訂購。
    但是總數num有一個上限。
    就是S*num>=∑si
    且num最小。
    也就是說相當於給你一個num;
    讓你求兩種披薩各要買多少個a,b(a+b==num)。
    使得參賽者的幸福感儘可能高。
    (參賽者即可以選擇第一種、也可以同時吃第二種)


【題解】


    如果每個人想要的披薩片數和<=S
    那麼只能買一塊披薩。
    則買a或買b,那麼每個人都只能選A或選B
    取較大值就好。

    矛盾點在哪裡?
        雖然你的a大,但是已經沒有A披薩給你了
        或者,全都買B披薩更合適

    列舉最後買了多少個B披薩
    i=0
    則一開始全都吃A披薩
    i=1
    則讓B-A前S大的人變成吃B披薩
    i=2
    則讓B-A前2S大的人變成吃B披薩
    在i=i-1的基礎上改一下就好
        記錄到了哪一個人,那個人還能有多少從A->B

    i最大為num,且i*S<=∑si

    以B-A為關鍵字降序排一下?


【錯的次數】


0

【反思】


加註釋的方式真的很好。。

【程式碼】

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 1e5;

struct abc{
    ll a,b,rest,c;
};

int n;
ll S,temp,num,srest,ans;
abc a[N+10];

bool cmp(abc a,abc b)
{
    return a.c > b.c;
}

int main()
{
    //freopen("F:\\rush.txt","r",stdin);
    scanf("%d%lld",&n,&S);
    for (int i = 1;i <= n;i++){
        scanf("%lld%lld%lld",&a[i].rest,&a[i].a,&a[i].b);
        a[i].c = a[i].b-a[i].a;
        temp += a[i].rest*a[i].a;
        srest += a[i].rest;
    }

    //獲取最多買多少個披薩
    ll l = 1,r = 1e10 + 100;
    while (l <= r)
    {
        ll mid = (l+r)>>1;
        ll temp = mid*S;
        if (temp >= srest)
        {
            num = mid;
            r = mid - 1;
        }else
            l = mid + 1;
    }

    sort(a+1,a+1+n,cmp);//按照B-A降序排

    ans = temp;//一開始全都吃A披薩
    //這個num的值可能會很大,你可能不能列舉出來全部。
    //在可以的情況下,儘量貪心選。只要B披薩的數目不超過num就可以了
    ll temp1 = 0,tnum = 0;//tnum是之前剩餘的變成B更優的個數
    //temp1是那些變成B之後答案的遞增值

    ll spe = num*S-srest;//多買的披薩的片數
    //S片才能湊夠一個B披薩

    for (int i = 1;i <= n;i++)
    {
        //第i個人有一些變成選B披薩

        if (a[i].c < 0)//這個人變成B之後答案會減小
        {//用spe儘量不讓他減小->本來肯定要有一些人從a變成B,現在可以用多餘的填,不用讓他們變了
            //前提是
            if (tnum + a[i].rest + spe >= S)//夠湊了
            {
                ll dd = tnum + spe;
                if (dd >= S)//不用加就能超過了
                {
                    ans = max(ans,temp + temp1);
                }else
                {
                    ll js = S-dd;
                    ans = max(ans,temp + temp1 + a[i].c*js);
                }
                temp1 = 0;
                break;
            }
        }
        if (tnum + a[i].rest < S)//如果沒有超過了1塊披薩的量
        {
            temp1 += a[i].rest*a[i].c;
            tnum += a[i].rest;
        }else//超過了
        {
            //這一個人的a->b選了多少個?
            temp += temp1;//優先加之前的

            ll rr = (a[i].rest + tnum)%S;//剩餘的
            ll choose = a[i].rest-rr;//之後再加當前的
            temp += choose*a[i].c;

            tnum = rr;//前些輪的剩餘變成rr了
            temp1 = rr*a[i].c;//temp1變成rr*a[i].c了

            ans = max(ans,temp);//取最大值
            //顯然這個過程中B是肯定夠用的
        }
    }
    //temp1 = 1;
    //printf("%lld\n",temp1);
    if (tnum + spe >= S)//全為正數最後一個剩下的可能還可以和多餘的湊出來一個S
    {
        ans = max(ans,temp + temp1);
    }

    printf("%lld\n",ans);
    return 0;
}


轉載於:https://www.cnblogs.com/AWCXV/p/7625976.html

相關文章