結果

問題 No.5025 Forest Fire
コンテスト
ユーザー mikuhatsune0831
提出日時 2026-10-24 17:18:27
言語 C++23(gcc16)
(gcc 16.2.0 + boost 1.92.0 + ACL)
コンパイル:
g++-16 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 2 ms / 2,000 ms
+ 881µs
コード長 6,337 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 2,224 ms
コンパイル使用メモリ 236,808 KB
実行使用メモリ 9,928 KB
スコア 74,190,287
最終ジャッジ日時 2026-10-24 17:22:38
合計ジャッジ時間 59,386 ms
ジャッジサーバーID
(参考情報)
judge5_0 / <nil>
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 50
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <algorithm>
#include <cstdint>
#include <functional>
#include <iostream>
#include <random>
#include <stdexcept>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

namespace firebreak {

struct Problem {
    int n;
    int k;
    std::vector<std::vector<int>> d;
};

using Cell = std::pair<int, int>;
using Priority = std::tuple<int, int, int>;
using DrawIndex = std::function<std::size_t(std::size_t)>;

constexpr int di[] = {-1, 1, 0, 0};
constexpr int dj[] = {0, 0, -1, 1};

template <class NextReal, class BeginRound>
std::size_t draw_unique_max(std::size_t count, NextReal&& next_real,
                            BeginRound&& begin_round) {
    if (count == 0) {
        throw std::runtime_error("Cannot draw from an empty candidate set");
    }
    for (;;) {
        begin_round();
        double maximum = -1.0;
        std::size_t winner = 0;
        bool maximum_is_tied = false;
        for (std::size_t index = 0; index < count; ++index) {
            const double value = next_real();
            if (value > maximum) {
                maximum = value;
                winner = index;
                maximum_is_tied = false;
            } else if (value == maximum) {
                maximum_is_tied = true;
            }
        }
        if (!maximum_is_tied) {
            return winner;
        }
    }
}

class UniformMaxDraw {
public:
    explicit UniformMaxDraw(std::uint64_t first_seed) : next_seed_(first_seed) {}

    std::size_t operator()(std::size_t count) {
        return draw_unique_max(
            count,
            [this] { return distribution_(engine_); },
            [this] {
                // One seed per draw over the tied candidates, including retries.
                engine_.seed(next_seed_++);
                distribution_.reset();
            });
    }

private:
    std::mt19937_64 engine_;
    std::uniform_real_distribution<double> distribution_{0.0, 1.0};
    std::uint64_t next_seed_;
};

bool inside(int n, int i, int j) {
    return 0 <= i && i < n && 0 <= j && j < n;
}

Problem read_problem(std::istream& in) {
    Problem p{};
    if (!(in >> p.n >> p.k) || p.n <= 0 || p.k < 0) {
        throw std::runtime_error("Invalid N or K");
    }
    p.d.assign(p.n, std::vector<int>(p.n));
    for (int i = 0; i < p.n; ++i) {
        std::string row;
        if (!(in >> row) || row.size() != static_cast<std::size_t>(p.n)) {
            throw std::runtime_error("Invalid grid row");
        }
        for (int j = 0; j < p.n; ++j) {
            if (row[j] < '0' || row[j] > '9') {
                throw std::runtime_error("Invalid dryness value");
            }
            p.d[i][j] = row[j] - '0';
        }
    }
    return p;
}

Priority priority_at(const Problem& p, int i, int j) {
    const int value = p.d[i][j];
    int neighbor_sum = 0;
    int squared_difference_sum = 0;
    for (int direction = 0; direction < 4; ++direction) {
        const int ni = i + di[direction];
        const int nj = j + dj[direction];
        const int neighbor = inside(p.n, ni, nj) ? p.d[ni][nj] : 0;
        neighbor_sum += neighbor;
        const int difference = value - neighbor;
        squared_difference_sum += difference * difference;
    }

    // Compare 4*S and 4*R as integers; this preserves the specified ordering.
    const int four_s = 4 * value + neighbor_sum;
    const int twice_i_offset = 2 * i - (p.n - 1);
    const int twice_j_offset = 2 * j - (p.n - 1);
    const int four_r = twice_i_offset * twice_i_offset
                     + twice_j_offset * twice_j_offset;
    return {-four_s, squared_difference_sum, four_r};
}

std::vector<Cell> select_trees(Problem p, const DrawIndex& draw_index) {
    std::vector<Cell> trees;
    for (int i = 0; i < p.n; ++i) {
        for (int j = 0; j < p.n; ++j) {
            if (p.d[i][j] > 0) {
                trees.emplace_back(i, j);
            }
        }
    }
    if (trees.size() <= static_cast<std::size_t>(p.k)) {
        return trees;
    }

    std::vector<std::vector<Priority>> priorities(
        p.n, std::vector<Priority>(p.n));
    for (int i = 0; i < p.n; ++i) {
        for (int j = 0; j < p.n; ++j) {
            priorities[i][j] = priority_at(p, i, j);
        }
    }

    std::vector<Cell> selected;
    selected.reserve(p.k);
    for (int step = 0; step < p.k; ++step) {
        std::vector<Cell> ties;
        Priority best{};
        for (int i = 0; i < p.n; ++i) {
            for (int j = 0; j < p.n; ++j) {
                if (p.d[i][j] == 0) {
                    continue;
                }
                const Priority& key = priorities[i][j];
                if (ties.empty() || key < best) {
                    best = key;
                    ties.clear();
                    ties.emplace_back(i, j);
                } else if (key == best) {
                    ties.emplace_back(i, j);
                }
            }
        }

        std::size_t index = 0;
        if (ties.size() > 1) {
            index = draw_index(ties.size());
            if (index >= ties.size()) {
                throw std::runtime_error("Tie draw returned an invalid index");
            }
        }
        const auto [i, j] = ties[index];
        selected.emplace_back(i, j);
        p.d[i][j] = 0;
        for (int direction = 0; direction < 4; ++direction) {
            const int ni = i + di[direction];
            const int nj = j + dj[direction];
            if (inside(p.n, ni, nj)) {
                priorities[ni][nj] = priority_at(p, ni, nj);
            }
        }
    }
    return selected;
}

void write_answer(std::ostream& out, const std::vector<Cell>& cells) {
    out << cells.size() << '\n';
    for (const auto& [i, j] : cells) {
        out << i << ' ' << j << '\n';
    }
}

}  // namespace firebreak

#ifndef FIREBREAK_NO_MAIN
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    try {
        auto problem = firebreak::read_problem(std::cin);
        firebreak::UniformMaxDraw draw(1);
        const auto answer = firebreak::select_trees(
            std::move(problem),
            [&draw](std::size_t count) { return draw(count); });
        firebreak::write_answer(std::cout, answer);
        return 0;
    } catch (const std::exception& error) {
        std::cerr << error.what() << '\n';
        return 1;
    }
}
#endif
0