题意:怪兽A攻击力为a1,血量为h1,怪兽B攻击力为a2,血量为h2,奥特曼的第 i 秒的攻击力为 i,问在打死两个怪兽之前奥特曼受到的最小伤害,在伤害值最小的前提下求字典序最小的攻击顺序。
思路:受到的伤害值最小肯定不能俩怪兽一会儿打一下,那必然是打完一个再去打另一个,所以考虑先打死A还是先打死B。
首先求出打死AB的最短时间all,打死A的最短时间a,打死B的最短时间b
1、先打死A
(1)这时花费了a的时间打死了A,若剩余all - a秒可以打死B,就是[1, a]打A,[a + 1, all]打B
(2)如果剩余all - a秒不够打死B,说明第a秒浪费了攻击力,求出浪费值x,可以在第x秒去打B,这样第a秒可以刚好打死A,不会有浪费。所以是[1, x - 1]打A,x打B,[x + 1, a]打A,[a + 1, all]打B
2、先打死B
需要花费b的时间打死B,求出在第b秒浪费的攻击力y,用这y点攻击力尽可能多地在打B之前打A,假设把第1~k秒都改为打A(1 + ... + k <= y)
(1)如果前k秒和第b + 1秒到第all秒足够打死A,就是[1, k]打A,[k +1, b]打死B,[b + 1, all]打死A
(2)如果前k秒和第b + 1秒到第all秒不够打死A,说明在第b秒依旧有浪费,假设还缺 z 的攻击,把前k秒的A往后移,给A造成更大的攻击,为保证字典序最大,只移动第k秒的A,让第k秒打B,第k + z秒打A。所以是[1, k - 1]打A,[k, k + z - 1]打B,k + z打A,[k + z + 1, b]打死B,[b + 1, all]打死A。
这里可以发现一个问题,为什么在1(2)中是求浪费值,而2(2)中是求差值,打死前一个怪兽的浪费值是 >= 打死后一个怪兽的差值的,由于1是先打A,为保证字典序最小,让位置尽可能靠后的换成B,也就是把浪费值用来打B;而2中为保证字典序最小,尽量让A靠前,也就是更换为打A的攻击力尽可能小,所以是差值
好好好好好题
一篇好懂的题解:https://www.cnblogs.com/TheRoadToTheGold/p/14032874.html(最后一种情况不太对,我在这儿改了一下)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 1e5 + 7;
ll p[N];
int main() {
for(int i = 1; i < N; ++i) p[i] = p[i - 1] + i;
int t;
ll a1, a2, h1, h2;
scanf("%d", &t);
while(t--) {
scanf("%lld%lld%lld%lld", &h1, &h2, &a1, &a2);
string s1 = "", s2 = "";
ll ans1 = 0, ans2 = 0;
ll all = lower_bound(p + 1, p + N, h1 + h2) - p;///总
ll a = lower_bound(p + 1, p + N, h1) - p; ///A
ll b = lower_bound(p + 1, p + N, h2) - p; ///B
///A
ll now = p[all] - p[a];
ans1 = a * a1 + all * a2;
if(now >= h2) {
string tmp1(a, 'A'), tmp2(all - a, 'B');
s1 = tmp1 + tmp2;
}
else {
ll lost = p[a] - h1;
string tmp1(lost - 1, 'A'), tmp2(a - lost, 'A'), tmp3(all - a, 'B');
s1 = tmp1 + "B" + tmp2 + tmp3;
}
///B
now = p[all] - p[b];
ll lost = p[b] - h2, it;
ans2 = b * a2 + all * a1;
it = upper_bound(p + 1, p + N, lost) - p - 1;
ll sum = p[it] + now;
if(sum >= h1) {
string tmp1(it, 'A'), tmp2(b - it, 'B'), tmp3(all - b, 'A');
s2 = tmp1 + tmp2 + tmp3;
}
else {
ll left = h1 - now;
for(int i = 1; i <= b; ++i) {
if(left >= 2 * i + 1 || left == i) {
left -= i;
s2 += "A";
}
else s2 += "B";
}
string tmp(all - b, 'A');
s2 += tmp;
}
if(ans1 < ans2) cout<<ans1<<' '<<s1<<'\n';
else if(ans1 > ans2) cout<<ans2<<' '<<s2<<'\n';
else {
if(s1 < s2) cout<<ans1<<' '<<s1<<'\n';
else cout<<ans2<<' '<<s2<<'\n';
}
}
return 0;
}