gym102028 2018ICPC焦作B Ultraman vs. Aodzilla and Bodzilla

题目链接

题目:

给定两个怪物 A A A B B B,怪 A A A有血量 H P A HP_A HPA和攻击 A T K A ATK_A ATKA,怪 B B B有血量 H P B HP_B HPB和攻击 A T K B ATK_B ATKB。有一个人打怪,第 i i i秒会发生两件事情:先是仍然存活的怪会对人造成其攻击的伤害,然后人可以选择一只还存活的怪并对这只怪造成 i i i点伤害。当怪的血量小于等于0的时候,怪就死了。问满足人受到的伤害最小的情况下的最小字典序的攻击序列。
( 1 ≤ H P A , H P B , A T K A , A T K B ≤ 1 0 9 ) (1 \le HP_A,HP_B,ATK_A,ATK_B \le 10^9) (1HPA,HPB,ATKA,ATKB109)

题解:

由于每秒钟只能打一只怪,所以怪肯定会先后死亡。设先死的怪为 p p p,后死的怪为 q q q,那么最小的满足 t ( t + 1 ) 2 ≥ H P p + H P q \frac{t(t+1)}{2} \ge HP_p+HP_q 2t(t+1)HPp+HPq t t t就是 q q q的最早死亡时间,最小的满足 t 1 ( t 1 + 1 ) 2 ≥ H P p \frac{t_1(t_1+1)}{2} \ge HP_p 2t1(t1+1)HPp t 1 t_1 t1就是 p p p的最早死亡时间,后者是显然的,前者是由于如果 H P p > ∑ i = t 1 + 1 t 2 i HP_p > \sum_{i=t_1+1}^{t_2}i HPp>i=t1+1t2i,那么肯定是因为对怪 p p p的伤害溢出了很多,又由于怪 p p p受到的是前 t 1 t_1 t1秒的所有伤害,所以肯定可以在前 t 1 t_1 t1秒中取出一个时间 k = ∑ i = 1 t 1 i − H P p k=\sum_{i=1}^{t_1}i-HP_p k=i=1t1iHPp,将这个时间的攻击给怪 q q q,这样对怪 p p p的伤害就是没有溢出的,又由于 t ( t + 1 ) 2 ≥ H P p + H P q \frac{t(t+1)}{2} \ge HP_p+HP_q 2t(t+1)HPp+HPq,所以剩余的伤害足够将怪 q q q打死。综上,确定了怪死亡的先后顺序以后,两个怪的死亡时间也可以确定,所以最小伤害也可以确定。
A A A先死还是 B B B先死两种情况,设攻击序列为 Q Q Q
假设 A A A先死,贪心地考虑字典序最小,先令 Q 1... t 1 = A Q_{1...t_1}=A Q1...t1=A Q t 1 + 1... t = B Q_{t_1+1...t}=B Qt1+1...t=B,这样有可能会打不死 B B B,那么将 k = ∑ i = 1 t 1 i − H P A k=\sum_{i=1}^{t_1}i-HP_A k=i=1t1iHPA这一秒的攻击分配给 B B B即可。因为这是满足 A , B A,B A,B均能死亡的最大的 k k k,这样可以使字典序最小。
假设 B B B先死,那么先令 Q 1... t 1 = B Q_{1...t_1}=B Q1...t1=B Q t 1 + 1... t = A Q_{t_1+1...t}=A Qt1+1...t=A,如果对 B B B的伤害有溢出,那么可以从1开始贪心的考虑移除这一秒的伤害,能不能打死怪 B B B,如果可以,那么就将这一秒分配给怪 A A A,直到不能移除了为止,假设恰好不能移除的这一秒为 k k k。这样以后怪 A A A还是有可能伤害不够,那么将 k − 1 k-1 k1秒对 B B B的伤害变成 k + ∑ i = t 1 + 1 t i + ∑ i = 1 k − 1 i − H P B − 1 k+\sum_{i=t_1+1}^{t}i+\sum_{i=1}^{k-1}i-HP_B-1 k+i=t1+1ti+i=1k1iHPB1这一秒的伤害即可,注意这里不能用移除 A A A的溢出伤害的时间来处理,因为怪 B B B不够的伤害要小于等于怪 A A A溢出的伤害,所以用怪 B B B不够的伤害的字典序更小。

复杂度: O ( max ⁡ { H P A , H P B } ) O(\sqrt{\max \{HP_A,HP_B\}}) O(max{HPA,HPB} )
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int T,tp;
pii hp,atk;
ll sum[maxn];
char ans1[maxn],ans2[maxn];
int main(void){
	// freopen("in.txt","r",stdin);
	ll lim=3e9;
	tp=0;
	while(sum[tp]+tp+1<=lim){
		++tp;
		sum[tp]=sum[tp-1]+tp;
	}
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d%d",&hp.fi,&hp.se,&atk.fi,&atk.se);
		//先A后B
		int pos=lower_bound(sum+1,sum+tp+1,hp.fi+hp.se)-sum;
		int pos2=lower_bound(sum+1,sum+tp+1,hp.fi)-sum;
		ll dam1=1ll*atk.fi*pos2+1ll*atk.se*pos;
		for(int i=1;i<=pos2;i++){
			ans1[i]='A';
		}
		for(int i=pos2+1;i<=pos;i++){
			ans1[i]='B';
		}
		if(sum[pos]-sum[pos2]<hp.se){
			ll dif=sum[pos2]-hp.fi;
			ans1[dif]='B';
		}
		ans1[pos+1]='\0';
		//先B后A
		pos2=lower_bound(sum+1,sum+tp+1,hp.se)-sum;
		ll rem=sum[pos2],dam2=1ll*atk.se*pos2+1ll*atk.fi*pos;
		int tt=pos2+1;
		ll cur=0;
		for(int i=1;i<=pos2;i++){
			if(rem-i>=hp.se){
				rem-=i;
				ans2[i]='A';
				cur+=i;
			}
			else{
				tt=i;
				break;
			}
		}
		for(int i=tt;i<=pos2;i++){
			ans2[i]='B';
		}
		for(int i=pos2+1;i<=pos;i++){
			ans2[i]='A';
		}
		cur+=sum[pos]-sum[pos2];
		if(cur<hp.fi){
			ll dif=hp.fi-cur;
			ans2[tt+dif-1]='A';
			ans2[tt-1]='B';
		}
		ans2[pos+1]='\0';
		if(dam1<dam2){
			printf("%lld %s\n",dam1,ans1+1);
		}
		else if(dam1>dam2){
			printf("%lld %s\n",dam2,ans2+1);
		}
		else{
			if(strcmp(ans1+1,ans2+1)<0){
				printf("%lld %s\n",dam1,ans1+1);
			}
			else{
				printf("%lld %s\n",dam1,ans2+1);
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值