輪郭をなぞるだけのブログ

浅学菲才のためにおそらく嘘も多い

AGC017 B問題 Moderate Differences

atcoder.jp


【問題の概要】
(N,A,B,C,D) が与えられる。A, B を両端とした要素数 N 個の数列を考える。
このとき、隣り合う要素の差が C 以上 D 以下であるような数列を作ることは可能であるかを判定せよ。
未定の要素に対して、大小の制限はない。


【考察】
よく分からないので、要素数が少ないサンプルケースでとりあえず実験する。
(N,A,B,C,D)=(4,7,6,4,5)のとき、要素の候補を考えてみる。

要素の候補を木のようにして遷移させてみると、図のようになる。
子ノードの要素は親ノードの各要素に対して、左側ではより小さく、右側ではより大きくなるように処理を施す。
ノード内で重複する数字も発生するが、まとめている。
また、より深くしてみると分かるが、左側の子ノードの一部の要素が親ノードの一部の要素以上になったり、右側の子ノードの一部の要素が親ノードの一部の要素以下になったりすることもある。


f:id:OutLine:20191204203017p:plain
重複するノードをまとめて、ノードに最小値と最大値だけを書くと次のようになる。(同じノードにある数字は連続するので、ノードの情報をこのように変更しても問題はない。)


f:id:OutLine:20191204203020p:plain
このサンプルケースはNOを出力するものだが、図より確かに 6 は数列の最後の要素にならない。

この実験を一般化すると図のようになる。
子ノードに遷移するとき、
左側 : 親ノードの最小値に-D、最大値に-C
右側 : 親ノードの最小値に+C、最大値に+D
の処理を行う。
f:id:OutLine:20191204211104p:plain

実験してみると、同じ深さのノード間で重複する部分があるように見て取れたが、各ノードに上記の処理をしていると解釈すると、CD の加算・減算の順序が違うだけなので、同じ深さのノードの重複部分はまとめることができ、パスカルの三角形のようなグラフになる。

よって、隣り合う要素の条件と最初の要素から、最後の要素のあり得る範囲が分かる。
深さ d のノード数は d+1 個あり、深さ di(\in[0,d]) 番目ノードに関して、
最小値 : A + i\times C + (i-d)\times D
最大値 : A + (i-d)\times C + i\times D
となる。
(ノードを左から見ていくと、最小値に関しては、
(Cの係数, Dの係数) = (0, -d), (1, 1-d), ..., (d, 0)
最大値に関しては、
(Cの係数,Dの係数) = (-d, 0), (1-d, 1), ..., (0, d)
となっていて、ここから類推もできる。)
判定にはこれを使う。
i に対して、その範囲に最後の要素が含まれているかを判定すれば、問題の数列を作ることが可能か判定できる。
計算量は O(N) になる。

コードにすると簡単だが、考察が疲れる。
最初は、AB の大小関係をなんとなく swap して固定していたが、別に必要なかった。
横着してすべて 64bit 整数にしてしまった。

#include <iostream>
using namespace std;

int main(){
    long long N, A, B, C, D;
    cin >> N >> A >> B >> C >> D;
    // 深さN-1のノードに対して判定処理を行う。
    for(long long i = 0; i < N; ++i){
        // lower_bound
        long long lb = A + i * C + (i - (N - 1)) * D;
        // upper_bound
        long long ub = A + (i - (N - 1)) * C + i * D;
        if(lb <= B && B <= ub){
            cout << "YES\n";
            return 0;
        }
    }
    cout << "NO\n";
}


【所感】
算数ゲーできません。
5年ぶりくらいに勉強してるのでツライ。
大学入る前は辛うじて数学力あったかもしれないが、その時期ならすぐに解けたりするんだろうか。
あんまり変わらない気もするけど。
数学力はもしかしたら中学生の方があるか。
知識はないけど、意欲は今よりあった気がする。
意欲は何にも代えがたいものだと思うので。
やる気なさ過ぎて動画つけながらやってたら、これでほとんど一日が溶けた。