题目链接:https://codeforces.com/gym/102028/problem/B
奥特曼打怪兽,有怪兽 a a a和 b b b 分别对应的生命值为 H P a , H P b HP_a,HP_b HPa,HPb和攻击力 A T K a , A T K b ATK_a,ATK_b ATKa,ATKb,按回合制进行攻击,如果怪物活着,将对奥特曼先造成对应的伤害,在第 i i i回合奥特曼打怪的伤害为 i i i,问奥特曼杀死这两只怪兽后受到的最小伤害,并对应输出字典序最小的方案
在贪心上的最优方案一定是优先杀一个人,再杀另一个。
设 f ( x ) f(x) f(x)表示对一个怪兽造成 x x x点伤害的最小步数,就是等差数列求个和。
那么我们一定可以在 f ( H P a + H P b ) f(HP_a+HP_b) f(HPa+HPb)的时间将两者全部杀掉。
假设先杀 a a a我们总可以保证在 f ( H P a ) f(HP_a) f(HPa)的时间杀死 a a a并且不浪费一点攻击力,即在 [ 1 , f ( H P a ) ] [1,f(HP_a)] [1,f(HPa)]的时间穿插着打 b b b,并且杀死 a a a
接下来分两种策略,分别是先杀a和先杀b。
先用三次二分搜索找出 l e n , l e n 1 , l e n 2 len,len1,len2 len,len1,len2分别对应杀光,杀 a a a,杀 b b b对应的最小步数。
如果先杀 a a a的话,就是在 [ 1 , l e n 1 ] [1,len1] [1,len1]穿插打 b b b,并打死 a a a在 ( l e n 1 , l e n ] (len1,len] (len1,len]全部打 b b b
现在的问题就是如何分配打 b b b,保证这个字符串字典序最小。
现在有一个 n e d ned ned和 l e f lef lef表示还需要多少打 b b b(可能不需要),和打 a a a的部分可以给多少出去。
我们就倒序在 [ 1 , l e n 1 ] [1,len1] [1,len1]找,如果需要,并且有剩余,就替换为 B B B
第二种情况是先杀 b b b的策略,这种策略在 [ 1 , l e n 2 ] [1,len2] [1,len2]分配打法的时候思维上比较复杂= =
在贪心策略上,依旧考虑 n e d ned ned和 l e f lef lef。
顺序循环填 A A A的情况会出现当前如果填了 A A A之后后面无法再填A( l e f lef lef不够了),这个时候就需要移动一下填 A A A的位置到需要的地方。这种情况在第一个策略里面就不需要考虑,因为倒序循环不会出现这种情况。
#include
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define int ll
#define debug cout<<"fuck"<
const int mod=(int)1e9+7;
const int maxn=(int)3e5+5;
int a[maxn];
int len,len1,len2;
int n,t;
int h1,h2,a1,a2,hur1,hur2;
bool check(int x,int ned)
{
ll sum=(x+1)*x/2;
if(sum<ned)return 0;
else return 1;
}
int sum(int l,int r)
{
return (r-l+1)*(l+r)/2;
}
int cmp(string a,string b)
{
for(int i=1;i<min(a.size(),b.size());i++)
{
if(a[i]==b[i])continue;
else return a[i]>b[i]?1:2;
}
return a.size()>=b.size()?1:2;
}
int binary_search(int x)
{
int l=1,r=(int)1e9,mid;
while(l<r)
{
mid=(l+r)/2;
if(check(mid,x))r=mid;
else
{
l=mid+1;
}
}
return l;
}
string kill_a()
{
string s="";
s+='0';
int ned=h2;
int lef=sum(1,len1)-h1;
for(int i=1;i<=len;i++)
{
if(i<=len1)s+='A';
else
{
s+='B';
ned-=i;
}
}
for(int i=len1;i>=1&&ned>0;i--)
{
if(lef>=i)//如果需要,并且有剩余
{
lef-=i;
ned-=i;
s[i]='B';
}
}
return s;
}
string kill_b()
{
string s="";
s+='0';
int lef=0;
int ned=h1-sum(len2+1,len);
for(int i=1;i<=len;i++)
{
if(i<=len2)
{
s+='B';
lef+=i;
}
else s+='A';
}
lef-=h2;
for(int i=1;i<=len2&&lef>0;i++)
{
if(lef>=i)
{
if(lef-i<i+1&&ned-i>0)//如果这个位置修改为A之后,之后不能再修改了,但是还有需要
{
s[ned]='A';
//cout<<"i:"<
break;
}
lef-=i;
ned-=i;
s[i]='A';
}
}
return s;
}
void sout(string s)
{
for(int i=1;i<s.size();i++)
{
cout<<s[i];
}
cout<<endl;
}
signed main()
{
IOS
cin>>t;
while(t--)
{
cin>>h1>>h2>>a1>>a2;
len=binary_search(h1+h2);
len1=binary_search(h1);
len2=binary_search(h2);
hur1=len1*(a1+a2)+(len-len1)*a2;
hur2=len2*(a1+a2)+(len-len2)*a1;
string fir_kil_a=kill_a();
string fir_kil_b=kill_b();
//cout<
//cout<
if(hur1<hur2)
{
cout<<hur1<<' ';
sout(fir_kil_a);
}
else if(hur1>hur2)
{
cout<<hur2<<' ';
sout(fir_kil_b);
}
else
{
if(cmp(fir_kil_a,fir_kil_b)==1)//a大
{
cout<<hur2<<' ';
sout(fir_kil_b);
}
else
{
cout<<hur1<<' ';
sout(fir_kil_a);
}
}
}
return 0;
}