CF1091F New Year and the Mallard Expedition

题意

在一条路上有草地 ( G ) (G) (G) ( W ) (W) (W)和石头 ( L ) (L) (L)
草地:可以走也可以飞。
水:可以游也可以飞。
石头:只能飞过去。
游:每米需要 3 s 3s 3s,但能得到 1 1 1耐力。
走:每米需要 5 s 5s 5s,但能得到 1 1 1耐力。
飞:每米需要 1 s 1s 1s,且消耗 1 1 1耐力。

然后用 n n n对(类型+距离)数据描述了这段路,问你在耐力不能减为负的情况下从起点走完最小耗时。(保证肯定可以走到)

思路

可以知道:

  1. 如果飞过石头时耐力不够了,可以在之前来回走得到耐力。
  2. 如果耐力有的多可以将走或游换成飞。

于是先按照走草地游水飞石头(耐力不够来回走)算出最后剩下的耐力。
在用这些耐力依照最小需要改变一些路段的行动方式。
最小需要则可以通过当前路段到终点的石头和非石头的距离求出。
需要知道的一点是当长度为 l e n len len的一段路的运动方式改变时,多消耗的耐力是 l e n ∗ 2 len*2 len2
然后可以改动的最大长度就是 m i n ( n e e d [ i ] / 2 , l e n g t h [ i ] ) min(need[i]/2,length[i]) min(need[i]/2,length[i])了。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const int Mx=1e5+5;
char Map[Mx];
int n;
LL len[Mx],nd[Mx],ans;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&len[i]),len[i]*=2;
    scanf("%s",Map+1);
    LL ans=0,p=0,lst=1;bool hw=0;
    for(int i=1;i<=n;i++){
        switch(Map[i]){
            case 'G':ans+=len[i]*5,p+=len[i];break;
            case 'W':ans+=len[i]*3,p+=len[i],hw=1;break;
            case 'L':
                if(p>len[i])p-=len[i],ans+=len[i];
                else{
                    lst=i+1;
                    if(hw)ans+=len[i]+(len[i]-p)*3,p=0;
                    else ans+=len[i]+(len[i]-p)*5,p=0;
                }
        }
    }
    for(int i=n;i>=lst;i--){
        if(Map[i]=='L')nd[i-1]=max(len[i],nd[i]+len[i]);
        else nd[i-1]=max(0ll,nd[i]-len[i]);
    }
    LL t=0,tmp=0;
    for(int i=lst;i<=n;i++){
        if(Map[i]=='L')t-=len[i];
        else t+=len[i];
        if(Map[i]=='G'){
            tmp=min(len[i]*2,t-nd[i]);
            if(tmp<=p){
                p-=tmp;
                ans-=tmp*2;
                t-=tmp;
            }else{
                ans-=p*2;
                return printf("%lld",ans/2),0;
            }
        }
    }
    ans-=p;
    printf("%lld",ans/2);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值