予鈴

アウトプットとメモ書きの中間みたいな記事がたくさん出ます。

AOJ 2826 ゲームバランス

問題

ゲームバランス
これD問題らしい。まじ?

考察

最終的には、最大値のパラメータ X_{max}を求めれば良い。

・ある X_t,L_tについて、レベルの増加値 :  max(1,X_t - |L_t - s_i|)が最大となる敵を倒しても M回以上戦闘しなければならない。ただし、倒せる敵は s_i < X_t + L_tという条件を満たす。

・レベルの増減値を最大とするには、できるだけ自分のレベル  L に近い敵 s_jを倒せば良い。これは、二分探索で高速に求めることができる。

・パラメータ Xが大きければ大きいほど、敵を倒せる種類が増える&&初期値が大きいので、最後の敵を倒す戦闘回数が少なくなる。
明らかに小さいパラメータを選ぶと、必要な戦闘回数は増えるので、単調性がある。パラーメタ Xに関しても二分探索で最適な値を探索すれば良い。

・一方で、 Xに関する二分探索の結果が res + 1 \leq min(s_i)となる場合は、敵を一人も足せないため、-1
出力すれば良い

感想

 M回以上の戦闘ってのが厄介すぎる。
 M回あとに初めて max(s_i)を倒せるようになれば良い。

#include<bits/stdc++.h>
using namespace std;

template<class F,class T>
auto  maximize(T imin,T imax,F &f){
    while(imax - imin > 1){
        T mid = imin + (imax - imin)/2;
        if(f(mid)) imin = mid;
        else imax = mid;
    }
    return imin;
}
inline bool solve(){
    int N,M;
    scanf("%d %d",&N,&M);
    if(N + M  == 0) return false;
    vector<int>s(N);
    for(int i = 0; i < N; ++i) scanf("%d",&s[i]);
    auto f = [&](int X){
        int L = 1;
        bool f = false;
        for(int i  = 0; i < M;  ++i){
            if(L + X > s.back() && f ) return false;
            if(L + X > s.back())  f = true;
            auto l = lower_bound(s.begin(), s.end(),L+X);
            auto r = s.begin();
            auto itr = lower_bound(r,l,L);
            int tmp = 1;
            if(*itr < X + L) tmp = max(tmp,X - abs(L - *itr));
            if(itr != s.begin()) tmp = max(tmp,X - abs(L - *prev(itr)));
            L += tmp;
        }
        return true;
    };
    int ans = maximize(0, s.back() + 1,f);
    if(ans + 1 <= s.front()) ans = -1;
    printf("%d\n",ans);
    return true;
}
signed main(){ while(solve()); }