trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

D - AtCoder Express メモ

texキーワードリンクが張られて数式が認識されない場合があるため見づらくなってます。脳内補間おなしゃっす

問題

f:id:trsing:20200708214938p:plain D - AtCoder Express

考えたこと

便宜のためt_i区間iと書きます。

区間速度の修正

区間 i+1 の速度が [tex:v{i+1}] なので、区間 i で出しても良い速度は [tex: \min( v {i+1}+t_i,v_i )]となります (区間 i で [tex: v{i+1}+t{i}] より速かったら区間 i+1 で [tex:v{i+1}] より速くなります)。 [tex:v{N+1}]を0として最終区間からv_iを更新していきます。

f:id:trsing:20200708215120p:plain
[tex:v{i+1}+t_i]より速いと区間i+1で[tex:v{i+1}]を超える

場合分け

区間開始時の速度、区間の速度、次の区間の速度に応じて場合分けが必要です。

  • v_i \geq v_{i+1}の場合

区間の速度まで頑張ります

f:id:trsing:20200708221303p:plainf:id:trsing:20200708221336p:plain
v_iに到達する場合、しない場合

面積の計算は

Vr = Min(Vl + t, Vi[i]);//区間i終了時の速度
Adt = Vr - Vl;//加速時間。Vlは区間i開始時の速度
Cst = t - Adt;//v_iで走る時間
men = (Vl + Vr) * Adt / 2.0 + Cst * Vr;//面積
  • v_i>v_{i+1}の場合

区間i終了時にv_{i+1}以下になるように調整します。

f:id:trsing:20200708221522p:plainf:id:trsing:20200708221558p:plain
v_iに到達後減速、到達せずに減速

v_{i+1}より早くならない場合減速の必要はなく加速のみです

f:id:trsing:20200708221633p:plain
加速のみ

面積の計算は

Vr = Min(Vi[i + 1], Vl + t);
Vm = Vi[i];//区間iの最大速度。とりあえずv_iとする
Adt = Vm - Vl;
Dst = Vm - Vr;//減速時間
if (t < Adt + Dst)//v_iに到達しない場合
{
    //Vl+Adt'=Dst'+Vr、Adt'+Dst'=t
    Adt = (t + Vr - Vl) / 2.0;
    Dst = t - Adt;
    Vm = Vl + Adt;
}
men = (Vl + Vm) * Adt / 2.0 + Vm * (t - Adt - Dst) + (Vm + Vr) * Dst / 2.0;

コード

static public long[] Sarray() { return ReadLine().Split().Select(long.Parse).ToArray(); }
static void Main(string[] args)
{
    SetOut(new StreamWriter(OpenStandardOutput()) { AutoFlush = false });
    Solve();
    Out.Flush();
    Read();
}
static void Solve()
{
    var N = Sarray()[0];
    var Ti = Sarray();
    var Vi = Sarray();
    Vi = Vi.Concat(new long[] { 0 }).ToArray();
    for (var i = N - 1; 0 <= i; --i)
        Vi[i] = Min(Vi[i], Vi[i + 1] + Ti[i]);

    double Vl = 0, Vr = 0, sum = 0;
    for (var i = 0; i < N; ++i)
    {
        double men = 0;
        var t = Ti[i];
        if (Vi[i] + 0.1 < Vl) throw new Exception();
        if (Vi[i] <= Vi[i + 1])
        {
            Vr = Min(Vl + t, Vi[i]);
            var Adt = Vr - Vl;
            var Cst = t - Adt;
            men = (Vl + Vr) * Adt / 2.0 + Cst * Vr;
        }
        else
        {
            Vr = Min(Vi[i + 1], Vl + t);
            double Vm = Vi[i];
            double Adt = Vm - Vl;
            double Dst = Vm - Vr;
            if (t < Adt + Dst)
            {
                Adt = (t + Vr - Vl) / 2.0;
                Dst = t - Adt;
                Vm = Vl + Adt;
            }
            men = (Vl + Vm) * Adt / 2.0 + Vm * (t - Adt - Dst) + (Vm + Vr) * Dst / 2.0;
        }
        Vl = Vr;
        sum += men;
    }
    WriteLine($"{sum:f5}");
}

背景

結構いい感じの方法*1なのに場合分けでやってる人少ないっぽい?のでメモ。

*1:要出典