2019-2020 ICPC Southeastern European Regional Programming Contest (SEERC 2019) E. Life Transfer(大模拟)

传送门

在这里插入图片描述

题意

给你 n n n k k k表示 n n n个人要出去玩,每辆车可以带 k k k个人,再给你 l c {l_c} lc p c {p_c} pc l m {l_m} lm p m {p_m} pm分别表示开车需要达到的年龄,买一辆车的花费,骑摩托需要达到的年龄,买一辆摩托的花费(摩托不能载人,也就是一辆摩托只能一个人骑着去)。
接下来给你 t t t d d d,表示 n n n个人之间可以互相转移年龄,每转移 1 1 1岁花费为 t t t,每个人最多转移 d d d岁(一个人的年龄不能低于 1 1 1岁),最后给你 n n n个人的年龄 a i {a_i} ai
也就是说,每个人可以选择开车,或者被开车的人带着,或者自己骑摩托出去,问你所有人出去玩的最小花费。如果无法所有人都出行,输出-1。

所有数据x均有 1 ⩽ x ⩽ 1 0 5 1\leqslant x\leqslant10^5 1x105

思路

1、刚开始看到 n n n这个范围以为是 d p dp dp优化之类的东西,后来写了状态转移之后发现直接枚举开车的人的数量或者骑摩托的人的数量就行了,因为剩下的人的出行方式是确定的。
2、我们将每个人按照年龄从小到大排序,考虑贪心取年龄大的人开车,或者年龄大的人骑摩托。下面以贪心取年龄大的人开车为例:
①从小到大依次枚举开车出行的人数 c r cr cr,则有最多 c r × ( k − 1 ) cr\times(k-1) cr×(k1) 个人可以被带,设为 g z gz gz,剩下的 n − c r − g z n-cr-gz ncrgz 个人自己骑摩托出行,显然 g z gz gz个被带着的优先取年龄小的人更优。因此需要判断的就是开车的和骑摩托的人是否能够借到足够的年龄。
②开两个数组分别表示从年龄大的开始开车能借出的年龄,和从年龄小的开始自己骑车能借到的年龄,再开一个数组表示从年龄小的开始被带着能借出多少年龄(注意每个人年龄至少 1 1 1岁,而且最多借 d d d岁)。
③从小到大枚举开车的人的人数,将需要借的总年龄加起来,能借出的总年龄加起来,判断是否足够借,足够的话就答案取最小值。
④注意特判没有人骑摩托的情况和某个人需要借超过 d d d岁的情况。贪心取年龄大的人骑摩托类似,留给读者自行思考。
⑤总时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),也就是排序的复杂度。

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define fi first
#define se second
#define pb push_back
using namespace std;
const int maxn=4e5+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
ll n,m,k;
int a[maxn],c[maxn];
ll ans,tmp,cnt;
ll lc, pc, lm, pm;
ll t, d;
template <typename T>
inline void read(T &X){
    X=0;int w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    if(w) X=-X;
}
ll mjie[maxn], cjie[maxn];//后缀能借的年龄(两种车)
ll totj[maxn];//前缀观众能借的年龄
ll myao[maxn],cyao[maxn];//前缀需要借的年龄(两种车)
void solve(){
    read(n);
    read(k);
    read(lc);
    read(pc);
    read(lm);
    read(pm);
    read(t);
    read(d);
    ans = inf;
    rep(i,1,n){
        read(c[i]);
    }
    sort(c + 1, c + 1 + n);
    cjie[n + 1] = 0;
    mjie[n + 1] = 0;
    dep(i,n,1) {
        if(c[i]>=lm) {
            mjie[i] = mjie[i + 1] + min(c[i] - lm, d);
        }
        else
            mjie[i] = mjie[i + 1];
        if(c[i]>=lc) {
            cjie[i] = cjie[i + 1] + min(c[i] - lc, d);
        }
        else
            cjie[i] = cjie[i + 1];
    }
    rep(i,1,n){
        totj[i] = totj[i - 1] + min(d, 1LL * c[i] - 1);
        if(c[i]+d<lm)
            myao[i] = 0;
        else if(c[i]<lm){
            myao[i] = myao[i - 1] + lm - c[i];
        }
        else
            myao[i] = myao[i - 1];
        if(c[i]+d<lc)
            cyao[i] = 0;
        else if(c[i]<lc){
            cyao[i] = cyao[i - 1] + lc - c[i];
        }
        else
            cyao[i] = cyao[i - 1];
    }
    dep(i,n,1){
        if(c[i]+d<lc)
            break;
        int cr = n - i + 1;//cr汽车数量
        int gz = min(cr * (k - 1), n - cr);//gz观众数量(被开车的带着)
        int mt = n - cr - gz;//mt骑摩托车的人的数量
        ll kj = totj[gz] + cjie[n - cr + 1];//观众和开车的人能借到的年龄
        ll xy = cyao[n] - cyao[n - cr];//开车的需要借的年龄
        if(cr+gz>=n){//只有汽车和观众
            if(kj>=xy)
                ans = min(ans, cr * pc + xy * t);
            continue;
        }
        if (c[gz + 1] + d < lm)
            continue;
        kj += mjie[gz + 1] - mjie[n - cr + 1];//骑摩托车的人能借的年龄
        xy += myao[n - cr] - myao[gz];//骑摩托车的人需要借的年龄
        if (kj >= xy)
            ans = min(ans, pc * cr + pm * mt + xy * t);
    }
    //cout << "!!: " << ans << endl;
    dep(i,n,1){
        if(c[i]+d<lm)
            break;
        int mt = n - i + 1;
        int cr = min(n - mt, (n - mt + k - 1) / k);
        int gz = n - mt - cr;
        ll kj = totj[gz] + mjie[n - mt + 1];
        ll xy = myao[n] - myao[n - mt];
        if(cr==0&&gz)//不合法情况
            continue;
        if(cr==0){//只有摩托车
            if(kj>=xy)
                ans = min(ans, mt * pm + xy * t);
            continue;
        }
        if (c[gz + 1] + d < lc)
            continue;
        kj += cjie[gz + 1] - cjie[n - mt + 1];
        xy += cyao[n - mt] - cyao[gz];
        if (kj >= xy)
            ans = min(ans, pc * cr + pm * mt + xy * t);
    }
    if(ans==inf)
        ans = -1;
    printf("%lld\n", ans);
}
int main(){
/*
#ifdef ONLINE_JUDGE
#else
    freopen("D:/Temp/in.txt", "r", stdin);
#endif
*/
    // freopen("e://duipai//myout.txt","w",stdout);
    int T=1,cas=1;
    //read(T);
    while(T--){
        solve();
    }
    //system("pause");
    return 0;
}
/*
2 2
18 1000 16 1
5 3
16 15

2 2
23 10 15 5
2 2 9 20
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值