提出 #61199591


ソースコード 拡げる

#ifndef ATCODER // AtCoder 環境でない場合はデバッグモードを有効にする. コンパイルにすごく時間がかかる!
#define _GLIBCXX_DEBUG // out of rangeを検出してくれる。#include<bits/stdc++.h>の前に記述する必要あり
#endif
#include<bits/stdc++.h>
using namespace std;
struct IOSetup {
    IOSetup() {
        // 実行が高速化するコード
        cin.tie(nullptr);
        ios_base::sync_with_stdio(false);
        // 小数点以下10桁まで(四捨五入)
        cout << fixed << setprecision(10);
    }
} iosetup;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, a, b) for (int i = a; i < b; i++)
#define rrep2(i, b, a) for (int i = b; i > a; i--)
#define rep3(i, a, b, d) for (int i = a; i < b; i+=d)
#define rrep3(i, b, a, d) for (int i = b; i > a; i-=d)
#define between(x, a, b) (((a) <= (x)) && ((x) < (b)))

/*
セグメントツリーは、配列の区間に対するクエリ処理や更新を効率的に行うためのデータ構造です。
特に、区間の和や積、最小値や最大値といった集約情報を迅速に取得する際に便利です。
*/
#include <bits/stdc++.h> // 必要なヘッダファイルをインクルード
using namespace std;

// セグメント木の定義
template<class Monoid> // template<typename Monoid> と書いても同様に機能します。歴史的にclassが使われている。
struct SegmentTree {
    // function<double(int, int)> int 型の引数を2つ取って double 型の値を返す関数
    using Func = function<Monoid(Monoid, Monoid)>; // 二項演算子の型を定義
    // functionは関数を変数として扱う型
    

    // ここはpublic.
    // コアメンバー
    int N; // 配列のサイズ
    Func OP; // 操作を定義する関数
    Monoid IDENTITY; // 単位元

    // 内部データ
    int log, offset; // log は木の高さ、offset は葉の始まりを示す
    vector<Monoid> data; // セグメント木のデータを格納する配列

    // コンストラクタ
    SegmentTree() {} // デフォルトコンストラクタ
    SegmentTree(int n, const Func &op, const Monoid &identity) { // サイズn、操作op、単位元identityで初期化
        init(n, op, identity);
    }
    SegmentTree(const vector<Monoid> &v, const Func &op, const Monoid &identity) { // ベクタから初期化
        init(v, op, identity);
    }
    
    // 初期化関数
    void init(int n, const Func &op, const Monoid &identity) {
        N = n; // サイズを設定
        OP = op; // 操作を設定
        IDENTITY = identity; // 単位元を設定
        log = 0, offset = 1; // log と offset を初期化
        while (offset < N) { // n以上の最小の2の冪乗を返す. 
            ++log; // 木の深さを計算.
            offset <<= 1; // 葉の数を計算(2のべき乗).
            // N = 6 なら log = 3, offset = 8; N = 8 なら log = 3, offset = 8, N = 9 なら log = 4, offset = 16;
        }
        data.assign(offset * 2, IDENTITY); // offset * 2 で葉ノードと内部ノードの領域を確保、完全2分木構造を作成.
        // 葉ノードは、配列の下半分(通常 offset から 2 * offset の範囲)に配置されます。offset は葉ノードの開始インデックスを表します。
        // 内部ノードは、葉ノードよりも上の部分に格納され、offset - 1 より小さいインデックスに配置されます.
        // data[1] が根ノード
        // 5個の要素を10で初期化
        // vec.assign(5, 10); → 10, 10, 10, 10, 10;
        // vector<int> data(16)なら、
        // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
        // - |     内部ノード     |        葉ノード       |
    }
    
    // ベクタからの初期化
    void init(const vector<Monoid> &v, const Func &op, const Monoid &identity) {
        init((int)v.size(), op, identity); // サイズを取得して初期化
        build(v); // ベクタを使って木を構築
    }
    
    // ノードの値を親ノードに統合. 下から上に更新していくイメージ.
    void pull_update(int k) {
        data[k] = OP(data[k * 2], data[k * 2 + 1]); // 子ノードの値を使って親ノードの値を更新(1 - based indexed)
        // 葉ノードは、配列の下半分(通常 offset から 2 * offset の範囲)に配置されます.
        // 内部ノードは、葉ノードよりも上の部分に格納され、offset - 1 より小さいインデックスに配置されます.
    }
    
    // ベクタを使って木を構築
    void build(const vector<Monoid> &v) {
        assert(N == (int)v.size()); // サイズが一致することを確認. assert(False)だと、プログラムが強制終了.
        for (int i = 0; i < N; ++i) data[i + offset] = v[i]; // 葉に値を設定
        // 葉ノードは、配列の下半分(通常 offset から 2 * offset の範囲)に配置されます.
        for (int k = offset - 1; k > 0; --k) pull_update(k); // 親ノードを更新
    }
    
    // 木のサイズを返す
    int size() const {
        return N; // サイズを返す
    }
    
    // i番目の要素を取得
    Monoid operator [] (int i) const {
        return data[i + offset]; // オフセットを考慮して要素を返す
    }
    
    // A[i]をxにupdateする. iは0-indexed, O(log N)
    void update(int i, const Monoid &v) {
        assert(0 <= i && i < N); // インデックスの範囲を確認
        // オフセットを加算(0-indexed → 1-indexed)
        int k = i + offset;
        data[k] = v; // 値を更新
        while (k >>= 1) {
            pull_update(k); // 親ノードを更新
        }
        // k の値を2で割りながら親ノードに移動.
        // つまり k = 0は使わない!
    }
    
    // 区間 [l, r) のqueryを取得. lとrは0-indexed, O(log N)
    Monoid query(int l, int r) {
        assert(0 <= l && l <= r && r <= N); // 引数の範囲を確認
        Monoid val_left = IDENTITY, val_right = IDENTITY; // 左右の合計を初期化
        l += offset, r += offset; // オフセットを加算
        for (; l < r; l >>= 1, r >>= 1) { // lがr未満の間ループ. l と r を同時に2で割っていき、重なったら終了.
            if (l & 1) val_left = OP(val_left, data[l++]); // lが奇数のとき、左側の値を更新
            if (r & 1) val_right = OP(data[--r], val_right); // rが奇数のとき、右側の値を更新. 開区間のため、--rしている。
            // 例えば葉が11-14の時、11をそのまま2で割っていってしまうと、8, 9, 10の情報も交じってしまう。
            // それは望ましくないのでl++して二分木において右に移動することで回避する。
        }
        return OP(val_left, val_right); // 左右の合計を統合して返す
    }

    // 区間 [0, r) のqueryを取得. 引数いらない. 0とrは0-indexed, O(log N)
    Monoid all_query() {
        return data[1]; // 根ノードの値を返す(全体の合計)
    }
    
    // A[i]の値をgetする. iは0-indexed, O(log N)
    Monoid get(int i) {
        assert(0 <= i && i < N); // インデックスの範囲を確認
        return data[i + offset];
    }
    
    // A[i]の値を全てprintする. iは0-indexed, O(log N)
    void print_leaf_node() {
        for (int i = 0; i < N; i++) {
            cout << data[i + offset] << " ";
        }
        cout << endl;
    }
    
    // segment_treeの値(構造)をprintする. iは0-indexed, O(log N)
    void print_tree_node() {
        int next_endl = 2;
        for (int i = 1; i < data.size(); i++) {
            cout << data[i] << " ";
            if (i == next_endl - 1) {
                next_endl <<= 1;
                cout << endl;
            }
        }
        cout << endl;
    }
    
    // A[i]にxを加算する. iは0-indexed, O(log N)
    void add(int i, const Monoid &v) {
        assert(0 <= i && i < N); // インデックスの範囲を確認
        long long k = i + offset; // オフセットを加算
        data[k] = v + get(i); // 現在の値にvを加算
        while (k >>= 1) pull_update(k); // 親ノードを更新
    }
    // x = seg.prod(L, r) として、f(x) = True となる最大の r を求める
    // int res = seg.max_right([&](int x) -> bool { return V > x; }, L);
    int max_right_index(const function<bool(Monoid)> f, int l = 0) {
        if (l == N) return N; // lが範囲外の場合はNを返す
        l += offset; // lにオフセットを加算
        Monoid SUM = IDENTITY; // 合計を初期化
        do {
            while (l % 2 == 0) l >>= 1; // lが偶数の場合、親ノードに移動
            if (!f(OP(SUM, data[l]))) { // 条件を満たさない場合
                while (l < offset) {
                    l = l * 2; // 左の子ノードに移動
                    if (f(OP(SUM, data[l]))) { // 条件を満たす場合
                        SUM = OP(SUM, data[l]); // 合計を更新
                        ++l; // lを次の位置に移動
                    }
                }
                return l - offset; // 結果を返す
            }
            SUM = OP(SUM, data[l]); // 合計を更新
            ++l; // lを次に進める
        } while ((l & -l) != l);  // lが2のべき乗になるまでループ
        return N; // Nを返す
    }

    // get min l that f(get(l, r)) = True (0-indexed), O(log N)
    // f(IDENTITY) need to be True
    int min_left_index(const function<bool(Monoid)> f, int r = -1) {
        if (r == 0) return 0; // rが0の場合、0を返す
        if (r == -1) r = N; // rが-1の場合、全体のサイズを設定
        r += offset; // rにオフセットを加算
        Monoid SUM = IDENTITY; // 合計を初期化
        do {
            --r; // rを1減らす
            while (r > 1 && (r % 2)) r >>= 1; // rが奇数の間は親ノードに移動
            if (!f(OP(data[r], SUM))) { // 条件を満たさない場合
                while (r < offset) {
                    r = r * 2 + 1; // 右の子ノードに移動
                    if (f(OP(data[r], SUM))) { // 条件を満たす場合
                        SUM = OP(data[r], SUM); // 合計を更新
                        --r; // rを1減らす
                    }
                }
                return r + 1 - offset; // 結果を返す
            }
            SUM = OP(data[r], SUM); // 合計を更新
        } while ((r & -r) != r); // rが2のべき乗になるまでループ
        return 0; // 0を返す
    }
    
    // デバッグ用出力
    friend ostream& operator << (ostream &s, const SegmentTree &seg) {
        for (int i = 0; i < (int)seg.size(); ++i) { // サイズ分ループ
            s << seg[i]; // 各要素を出力
            if (i != (int)seg.size() - 1) s << " "; // 最後の要素でない場合、空白を追加
        }
        return s; // ストリームを返す
    }
};

/*
セグメントツリーの具体的な操作例と単位元:
操作:         query_func        単位元: ide_ele, 足し算でいう0, 掛け算でいう1.
最小値:       min(x, y)         単位元: float('inf')
最大値:       max(x, y)         単位元: -float('inf')
区間和:       x + y             単位元: 0
区間積:       x * y             単位元: 1
最大公約数:    gcd(x, y)        単位元: 0
最小公倍数:    lcm(x, y)        単位元: 1
*/

// main関数
int main() {
    // n: セグメント木のサイズ
    int n, m;
    cin >> n >> m;
    set<int> st;
    vector<tuple<int, int, char>> query;
    rep(i, m) {
        int x, y; char c;
        cin >> x >> y >> c;
        query.push_back({x, y, c});
    }
    for (auto& [x, y, c] : query) {
        st.insert(x);
    }
    map<int, int> d;
    int ind = 0;
    for (auto& i : st) {
        d[i] = ind;
        ind++;
    }

    m = st.size();
    vector<int> vec(m + 1);
    auto query_func = [&](int a, int b) { // やりたい操作を計算する関数
        return max(a, b); // やりたい操作を記述
    };
    int ide_ele = -(1 << 30);
    for (auto& [x, y, c] : query) {
        if (c == 'W') continue;
        ind = d[x];
        vec[ind] = max(y, vec[ind]);
    }
    SegmentTree<int> seg(vec, query_func, ide_ele); // セグメント木を初期化
    for (auto& [x, y, c] : query) {
        if (c == 'B') continue;
        ind = d[x];
        if ((seg.query(ind, m)) >= y) {
            cout << "No";
            return 0;
        }
    }
    cout << "Yes";
    // x = seg.prod(L, r) として、f(x) = True となる最大の r を求める
    // int res = seg.max_right([&](int x) -> bool { return V > x; }, L);
}

提出情報

提出日時
問題 D - Diagonal Separation
ユーザ kazuki00
言語 C++ 23 (gcc 12.2)
得点 425
コード長 13143 Byte
結果 AC
実行時間 188 ms
メモリ 27164 KiB

ジャッジ結果

セット名 Sample All
得点 / 配点 0 / 0 425 / 425
結果
AC × 4
AC × 63
セット名 テストケース
Sample 00_sample_00.txt, 00_sample_01.txt, 00_sample_02.txt, 00_sample_03.txt
All 00_sample_00.txt, 00_sample_01.txt, 00_sample_02.txt, 00_sample_03.txt, 01_test_00.txt, 01_test_01.txt, 01_test_02.txt, 01_test_03.txt, 01_test_04.txt, 01_test_05.txt, 01_test_06.txt, 01_test_07.txt, 01_test_08.txt, 01_test_09.txt, 01_test_10.txt, 01_test_11.txt, 01_test_12.txt, 01_test_13.txt, 01_test_14.txt, 01_test_15.txt, 01_test_16.txt, 01_test_17.txt, 01_test_18.txt, 01_test_19.txt, 01_test_20.txt, 01_test_21.txt, 01_test_22.txt, 01_test_23.txt, 01_test_24.txt, 01_test_25.txt, 01_test_26.txt, 01_test_27.txt, 01_test_28.txt, 01_test_29.txt, 01_test_30.txt, 01_test_31.txt, 01_test_32.txt, 01_test_33.txt, 01_test_34.txt, 01_test_35.txt, 01_test_36.txt, 01_test_37.txt, 01_test_38.txt, 01_test_39.txt, 01_test_40.txt, 01_test_41.txt, 01_test_42.txt, 01_test_43.txt, 01_test_44.txt, 01_test_45.txt, 01_test_46.txt, 01_test_47.txt, 01_test_48.txt, 01_test_49.txt, 01_test_50.txt, 01_test_51.txt, 01_test_52.txt, 01_test_53.txt, 01_test_54.txt, 01_test_55.txt, 01_test_56.txt, 01_test_57.txt, 01_test_58.txt
ケース名 結果 実行時間 メモリ
00_sample_00.txt AC 1 ms 3552 KiB
00_sample_01.txt AC 1 ms 3472 KiB
00_sample_02.txt AC 1 ms 3436 KiB
00_sample_03.txt AC 1 ms 3512 KiB
01_test_00.txt AC 1 ms 3616 KiB
01_test_01.txt AC 1 ms 3536 KiB
01_test_02.txt AC 1 ms 3636 KiB
01_test_03.txt AC 1 ms 3504 KiB
01_test_04.txt AC 46 ms 6176 KiB
01_test_05.txt AC 41 ms 6184 KiB
01_test_06.txt AC 35 ms 6332 KiB
01_test_07.txt AC 13 ms 4076 KiB
01_test_08.txt AC 19 ms 4800 KiB
01_test_09.txt AC 18 ms 4768 KiB
01_test_10.txt AC 141 ms 27084 KiB
01_test_11.txt AC 84 ms 18424 KiB
01_test_12.txt AC 140 ms 27056 KiB
01_test_13.txt AC 36 ms 10500 KiB
01_test_14.txt AC 187 ms 27016 KiB
01_test_15.txt AC 160 ms 24412 KiB
01_test_16.txt AC 184 ms 27056 KiB
01_test_17.txt AC 90 ms 16564 KiB
01_test_18.txt AC 144 ms 27072 KiB
01_test_19.txt AC 24 ms 7664 KiB
01_test_20.txt AC 164 ms 27072 KiB
01_test_21.txt AC 63 ms 13316 KiB
01_test_22.txt AC 180 ms 27016 KiB
01_test_23.txt AC 26 ms 7912 KiB
01_test_24.txt AC 175 ms 27128 KiB
01_test_25.txt AC 34 ms 9232 KiB
01_test_26.txt AC 154 ms 27080 KiB
01_test_27.txt AC 129 ms 23096 KiB
01_test_28.txt AC 181 ms 27056 KiB
01_test_29.txt AC 63 ms 14888 KiB
01_test_30.txt AC 168 ms 27056 KiB
01_test_31.txt AC 77 ms 15736 KiB
01_test_32.txt AC 187 ms 27000 KiB
01_test_33.txt AC 85 ms 15436 KiB
01_test_34.txt AC 116 ms 14900 KiB
01_test_35.txt AC 88 ms 12496 KiB
01_test_36.txt AC 114 ms 14892 KiB
01_test_37.txt AC 19 ms 5728 KiB
01_test_38.txt AC 103 ms 14860 KiB
01_test_39.txt AC 52 ms 10156 KiB
01_test_40.txt AC 103 ms 14880 KiB
01_test_41.txt AC 27 ms 6676 KiB
01_test_42.txt AC 107 ms 14868 KiB
01_test_43.txt AC 15 ms 5104 KiB
01_test_44.txt AC 122 ms 14872 KiB
01_test_45.txt AC 48 ms 8616 KiB
01_test_46.txt AC 188 ms 27104 KiB
01_test_47.txt AC 38 ms 9860 KiB
01_test_48.txt AC 158 ms 27164 KiB
01_test_49.txt AC 104 ms 21424 KiB
01_test_50.txt AC 169 ms 27100 KiB
01_test_51.txt AC 86 ms 18392 KiB
01_test_52.txt AC 158 ms 27044 KiB
01_test_53.txt AC 137 ms 24092 KiB
01_test_54.txt AC 114 ms 14808 KiB
01_test_55.txt AC 8 ms 4504 KiB
01_test_56.txt AC 92 ms 14808 KiB
01_test_57.txt AC 14 ms 5160 KiB
01_test_58.txt AC 1 ms 3544 KiB