kmjp's blog

競技プログラミング参加記です

TopCoder SRM 749 Div1 Easy FightMonsterDiv1

Mediumのツメが甘くまたレートダウン。
https://community.topcoder.com/stat?c=problem_statement&pm=15296

問題

残り体力がHである敵を倒すターン制ゲームを考える。
1ターンあたり1回攻撃でき、初回の攻撃では、Aだけダメージを与えられる。
以後、ダメージを与えるたびに以後の攻撃のダメージはAのL%だけ増加する(複利ではなく、毎回A*L/100だけ増加)。

ここで、ゲーム中1回だけ魔法を唱えることができる。
(魔法を唱えるのも1ターン消費し、そのターンは攻撃できない)

魔法を唱えると、以後Dターンの間、与えるダメージが本来の計算式の5倍になる。

攻撃と魔法の最適手を取る場合、最小何ターンで敵の体力を0以下にできるか。

解法

ターン数が多いほどダメージが多いので、二分探索で最小ターンを求めよう。
まず、魔法については1ターンで敵を倒せる場合以外は、必ずどこかで唱えた方が良い。
Lは最大100%なので、1ターン魔法を唱えることでダメージ倍加のチャンスを逃してもお釣りがくる。
さらに言えば、後半ほど与ダメージが増えるので、終盤Dターンにダメージが5倍になるようにしよう。

class FightMonsterDiv1 {
	public:
	ll H,A,A2,D;
	
	__int128_t dam(__int128_t v) {
		if(v==1) return A;
		v--;
		__int128_t ret=v*A;
		ret+=A2*v*(v-1)/2;
		
		__int128_t du=min((__int128_t)D,v);
		__int128_t cur=A+(v-1)*A2;
		ret+=4*cur*du;
		ret-=A2*du*(du-1)/2*4;
		
		
		return ret;
	}
	
	long long fightTime(long long hp, long long attack, int level, long long duration) {
		H=hp;
		A=attack;
		A2=A*level/100;
		D=duration;
		
		ll ret=1LL<<40;
		for(int i=39;i>=0;i--) if(dam(ret-(1LL<<i))>=H) ret-=1LL<<i;
		return ret;
	}
}

まとめ

問題文に対しサンプルが不親切でわかりにくい…。