2018-2019 ICPC焦作区域赛B - Ultraman vs. Aodzilla and Bodzilla

本题解借鉴了某大佬的博客,由于那篇博客里有点小错误,因此我这里重写一篇,希望大家在看到我的博客的同时也关注一下那位大佬。
大佬博客地址

题目链接

题解思路:很显然,要使奥特曼受到的伤害最小,肯定要先杀死一只怪兽,所以我们就分类讨论先杀A还是先杀B。

//不少人可能会说比较一下A,B的攻击力,先杀攻击力高的一定会更优。但这是错误的,比如一个100血10攻的怪和一只3血9攻的怪,显然是要先杀3血9攻的那只怪。

因此,我们就分类先杀A还是先杀B,然后比较两者的耗费和字典序,取最小即可。

首先处理一个前缀和的数组pri[i]表示1到i时刻的伤害总和。

1.先杀A

我们先算出最快打死A的回合:u1=lower_bound(pri+1,pri+100000,a.H)-pri
打死两个怪的最快时间:u2=lower_bound(pri+1,pri+100000,a.H+b.H)-pri

(1)哪怕不分A溢出的伤害,剩下的伤害也足以在u2-u1回合内打死B
这样肯定是1-u1是A,u1+1到u2是B字典序最小。

(2)pri[u2]-pri[u1]
在这种情况下,A溢出的伤害必然要打到B上,那应该在那一次打呢?自然是尽可能靠后,B越靠后字典序越小。

2.先杀B

算出最快打死B的回合:u1=lower_bound(pri+1,pri+100000,b.H)-pri
先杀B分伤害给A的情况处理起来就比较麻烦,因为要使A尽可能靠前,这里最好的情况就是在伤害值<=B溢出伤害时前面能插入一段连续的A,然后这段的伤害+pri[u2]-pri[u1]>=a.H,这样能保证B先死且A尽可能地靠前。可是如果不行呢,我们就只能把前面连续的A拆出来,往后移,让对A的伤害值变大,这样才能打死两只怪兽。

(1)一段A的前缀+pri[u2]-pri[u1]>=a.H
(2)在上述情况打不死的情况下,定义left=a.H-(pri[u2]-pri[u1]) (前面那部分所需要造成的伤害)。在1-u1的回合,当left>=2*i+1 || left==i 的时候插入A,left -= i,否则插入B,剩下的全是A。

#include

using namespace std;

#define maxn 300005
#define ll long long
const int mod = 1e9 + 7;

int T;
ll pri[maxn];
struct node{
    ll H,K;
}a,b;

void init(){
    for(int i=1;i<=1e5;i++)
        pri[i]=pri[i-1]+i;
}

int main()
{
    init();
    scanf("%d",&T);
    while(T--){
        ll suma=0,sumb=0;
        string ansa,ansb;
        scanf("%lld %lld %lld %lld",&a.H,&b.H,&a.K,&b.K);
        //A先死-
        int u1=lower_bound(pri+1,pri+100000,a.H)-pri;
        int u2=lower_bound(pri+1,pri+100000,b.H+a.H)-pri;
        ll left=pri[u1]-a.H;
        suma=a.K*u1+b.K*u2;
        if(pri[u2]-pri[u1]>=b.H){
            for(int i=1;i<=u1;i++)
                ansa+='A';
            for(int i=u1+1;i<=u2;i++)
                ansa+='B';
        }
        else{
            for(int i=1;i<=u1;i++){
                if(i==left)ansa+='B';
                else ansa+='A';
            }
            for(int i=u1+1;i<=u2;i++)
                ansa+='B';
        }
        //B先死
        u1=lower_bound(pri+1,pri+100000,b.H)-pri;
        u2=lower_bound(pri+1,pri+100000,a.H+b.H)-pri;
        sumb=b.K*u1+a.K*u2;
        left=pri[u1]-b.H;
        int pos=upper_bound(pri+1,pri+100000,left)-pri-1;
        if(pri[pos]+pri[u2]-pri[u1]>=a.H){
            for(int i=1;i<=pos;i++)
                ansb+='A';
            for(int i=pos+1;i<=u1;i++)
                ansb+='B';
            for(int i=u1+1;i<=u2;i++)
                ansb+='A';
        }
        else{
            left=a.H-(pri[u2]-pri[u1]);
            for(int i=1;i<=u1;i++){
                if(left>=2*i+1||left==i){
                    left-=i;
                    ansb+='A';
                }
                else ansb+='B';
            }
            for(int i=u1+1;i<=u2;i++)
                ansb+='A';
        }
        if(suma<sumb)cout<<suma<<" "<<ansa<<"\n";
        else if(sumb<suma)cout<<sumb<<" "<<ansb<<"\n";
        else{
            if(ansa<ansb)cout<<suma<<" "<<ansa<<"\n";
            else cout<<sumb<<" "<<ansb<<"\n";
        }
    }
    return 0;
}

你可能感兴趣的:(思维)