Submission #70299783
Source Code Expand
#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <deque>
#include <forward_list>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
class JsonDumper {
struct KeyValue {
std::string key;
std::string value;
};
std::vector<KeyValue> _items;
bool dump_at_end = false;
public:
JsonDumper(bool dump_at_end_ = false) : dump_at_end(dump_at_end_) {}
~JsonDumper() {
if (dump_at_end) std::cout << dump() << std::endl;
}
void set_dump_at_end() { dump_at_end = true; }
void operator()(const std::string &key, const std::string &value) {
_items.push_back(KeyValue{key, "\"" + value + "\""});
}
template <class T> void operator()(const std::string &key, T value) {
_items.push_back(KeyValue{key, std::to_string(value)});
}
std::string dump() const {
std::string ret = "{\n";
if (!_items.empty()) {
for (const auto &[k, v] : _items) ret += " \"" + k + "\": " + v + ",\n";
ret.erase(ret.end() - 2);
}
ret += "}";
return ret;
}
} jdump;
#define ALL(x) (x).begin(), (x).end()
#define FOR(i, begin, end) for (int i = (begin), i##_end_ = (end); i < i##_end_; i++)
#define IFOR(i, begin, end) for (int i = (end) - 1, i##_begin_ = (begin); i >= i##_begin_; i--)
#define REP(i, n) FOR(i, 0, n)
#define IREP(i, n) IFOR(i, 0, n)
template <typename T> bool chmax(T &m, const T q) { return m < q ? (m = q, true) : false; }
template <typename T> bool chmin(T &m, const T q) { return m > q ? (m = q, true) : false; }
int floor_lg(long long x) { return x <= 0 ? -1 : 63 - __builtin_clzll(x); }
template <class T1, class T2> T1 floor_div(T1 num, T2 den) {
return (num > 0 ? num / den : -((-num + den - 1) / den));
}
template <class T1, class T2> std::pair<T1, T2> operator+(const std::pair<T1, T2> &l, const std::pair<T1, T2> &r) {
return std::make_pair(l.first + r.first, l.second + r.second);
}
template <class T1, class T2> std::pair<T1, T2> operator-(const std::pair<T1, T2> &l, const std::pair<T1, T2> &r) {
return std::make_pair(l.first - r.first, l.second - r.second);
}
template <class T> std::vector<T> sort_unique(std::vector<T> vec) {
sort(vec.begin(), vec.end()), vec.erase(unique(vec.begin(), vec.end()), vec.end());
return vec;
}
template <class T> int arglb(const std::vector<T> &v, const T &x) {
return std::distance(v.begin(), std::lower_bound(v.begin(), v.end(), x));
}
template <class T> int argub(const std::vector<T> &v, const T &x) {
return std::distance(v.begin(), std::upper_bound(v.begin(), v.end(), x));
}
template <class IStream, class T> IStream &operator>>(IStream &is, std::vector<T> &vec) {
for (auto &v : vec) is >> v;
return is;
}
template <class OStream, class T> OStream &operator<<(OStream &os, const std::vector<T> &vec);
template <class OStream, class T, size_t sz> OStream &operator<<(OStream &os, const std::array<T, sz> &arr);
template <class OStream, class T, class TH> OStream &operator<<(OStream &os, const std::unordered_set<T, TH> &vec);
template <class OStream, class T, class U> OStream &operator<<(OStream &os, const std::pair<T, U> &pa);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::deque<T> &vec);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::set<T> &vec);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::multiset<T> &vec);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::unordered_multiset<T> &vec);
template <class OStream, class T, class U> OStream &operator<<(OStream &os, const std::pair<T, U> &pa);
template <class OStream, class TK, class TV> OStream &operator<<(OStream &os, const std::map<TK, TV> &mp);
template <class OStream, class TK, class TV, class TH>
OStream &operator<<(OStream &os, const std::unordered_map<TK, TV, TH> &mp);
template <class OStream, class... T> OStream &operator<<(OStream &os, const std::tuple<T...> &tpl);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::vector<T> &vec) {
os << '[';
for (auto v : vec) os << v << ',';
os << ']';
return os;
}
template <class OStream, class T, size_t sz> OStream &operator<<(OStream &os, const std::array<T, sz> &arr) {
os << '[';
for (auto v : arr) os << v << ',';
os << ']';
return os;
}
template <class... T> std::istream &operator>>(std::istream &is, std::tuple<T...> &tpl) {
std::apply([&is](auto &&...args) { ((is >> args), ...); }, tpl);
return is;
}
template <class OStream, class... T> OStream &operator<<(OStream &os, const std::tuple<T...> &tpl) {
os << '(';
std::apply([&os](auto &&...args) { ((os << args << ','), ...); }, tpl);
return os << ')';
}
template <class OStream, class T, class TH> OStream &operator<<(OStream &os, const std::unordered_set<T, TH> &vec) {
os << '{';
for (auto v : vec) os << v << ',';
os << '}';
return os;
}
template <class OStream, class T> OStream &operator<<(OStream &os, const std::deque<T> &vec) {
os << "deq[";
for (auto v : vec) os << v << ',';
os << ']';
return os;
}
template <class OStream, class T> OStream &operator<<(OStream &os, const std::set<T> &vec) {
os << '{';
for (auto v : vec) os << v << ',';
os << '}';
return os;
}
template <class OStream, class T> OStream &operator<<(OStream &os, const std::multiset<T> &vec) {
os << '{';
for (auto v : vec) os << v << ',';
os << '}';
return os;
}
template <class OStream, class T> OStream &operator<<(OStream &os, const std::unordered_multiset<T> &vec) {
os << '{';
for (auto v : vec) os << v << ',';
os << '}';
return os;
}
template <class OStream, class T, class U> OStream &operator<<(OStream &os, const std::pair<T, U> &pa) {
return os << '(' << pa.first << ',' << pa.second << ')';
}
template <class OStream, class TK, class TV> OStream &operator<<(OStream &os, const std::map<TK, TV> &mp) {
os << '{';
for (auto v : mp) os << v.first << "=>" << v.second << ',';
os << '}';
return os;
}
template <class OStream, class TK, class TV, class TH>
OStream &operator<<(OStream &os, const std::unordered_map<TK, TV, TH> &mp) {
os << '{';
for (auto v : mp) os << v.first << "=>" << v.second << ',';
os << '}';
return os;
}
#ifdef HITONANODE_LOCAL
const std::string COLOR_RESET = "\033[0m", BRIGHT_GREEN = "\033[1;32m", BRIGHT_RED = "\033[1;31m",
BRIGHT_CYAN = "\033[1;36m", NORMAL_CROSSED = "\033[0;9;37m", RED_BACKGROUND = "\033[1;41m",
NORMAL_FAINT = "\033[0;2m";
#define dbg(x) \
std::cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " \
<< __FILE__ << COLOR_RESET << std::endl
#define dbgif(cond, x) \
((cond) ? std::cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ \
<< ") " << __FILE__ << COLOR_RESET << std::endl \
: std::cerr)
#else
#define dbg(x) 0
#define dbgif(cond, x) 0
#endif
#ifdef BENCHMARK
#define dump_onlinejudge(x) 0
struct setenv {
setenv() { jdump.set_dump_at_end(); }
} setenv_;
#else
#define dump_onlinejudge(x) (std::cout << (x) << std::endl)
#endif
#include <algorithm>
#include <chrono>
#include <iostream>
#include <limits>
#include <random>
#include <string>
#include <utility>
#include <vector>
using namespace std;
struct PlanDetail {
vector<vector<int>> weapon_use;
vector<int> bare_hand;
int total_attacks;
};
struct ActionBuilder {
const vector<int> &H;
const vector<vector<int>> &A;
ActionBuilder(const vector<int> &hardness, const vector<vector<int>> &damage) : H(hardness), A(damage) {}
vector<pair<int, int>> build_actions(const vector<int> &order, const PlanDetail &plan) const {
vector<pair<int, int>> actions;
actions.reserve(plan.total_attacks);
vector<int> hardness = H;
int N = static_cast<int>(order.size());
for (int idx = 0; idx < N; ++idx) {
int chest = order[idx];
for (int prev = 0; prev < idx; ++prev) {
int weapon = order[prev];
int cnt = plan.weapon_use[weapon][chest];
for (int t = 0; t < cnt; ++t) {
if (hardness[chest] <= 0) break;
int dmg = min(A[weapon][chest], hardness[chest]);
if (dmg <= 0) break;
actions.emplace_back(weapon, chest);
hardness[chest] -= dmg;
}
}
int bare = plan.bare_hand[chest];
for (int t = 0; t < bare; ++t) {
if (hardness[chest] <= 0) break;
actions.emplace_back(-1, chest);
hardness[chest] -= 1;
}
while (hardness[chest] > 0) {
actions.emplace_back(-1, chest);
hardness[chest] -= 1;
}
}
return actions;
}
};
struct Solver {
int N;
vector<int> H;
vector<int> C;
vector<vector<int>> A;
mt19937_64 rng;
struct GraphState {
vector<vector<int>> use;
vector<int> out_used;
};
struct StateEvaluation {
bool valid = false;
int score = 0;
vector<int> order;
vector<int> bare_hand;
};
struct Ver15State {
vector<int> order;
GraphState graph;
vector<int> pos;
vector<long long> contribution;
vector<int> bare_hand;
long long score = (1LL << 60);
bool valid = false;
};
Solver() : rng(20241003ULL) {} // 固定 seed で再現性を確保
void read_input() {
cin >> N;
H.assign(N, 0);
C.assign(N, 0);
for (int i = 0; i < N; ++i) { cin >> H[i]; }
for (int i = 0; i < N; ++i) { cin >> C[i]; }
A.assign(N, vector<int>(N, 0));
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) { cin >> A[i][j]; }
}
}
int evaluate(const vector<int> &order) const {
vector<int> remaining(H.begin(), H.end());
int weapon_uses = 0;
for (int pos = N - 1; pos >= 0; --pos) {
int weapon = order[pos];
int limit = C[weapon];
for (int iter = 0; iter < limit; ++iter) {
int best_chest = -1;
int best_value = 1;
for (int nxt = pos + 1; nxt < N; ++nxt) {
int chest = order[nxt];
if (remaining[chest] <= 0) continue;
int val = remaining[chest];
int dmg = A[weapon][chest];
if (dmg < val) val = dmg;
if (val > best_value) {
best_value = val;
best_chest = chest;
}
}
if (best_chest == -1) break;
int dmg = min(A[weapon][best_chest], remaining[best_chest]);
if (dmg <= 1) break;
remaining[best_chest] -= dmg;
++weapon_uses;
}
}
long long total = weapon_uses;
for (int chest = 0; chest < N; ++chest) {
if (remaining[chest] > 0) { total += remaining[chest]; }
}
return static_cast<int>(total);
}
PlanDetail build_plan(const vector<int> &order) const {
PlanDetail plan;
plan.weapon_use.assign(N, vector<int>(N, 0));
plan.bare_hand.assign(N, 0);
vector<int> remaining(H.begin(), H.end());
int weapon_uses = 0;
for (int pos = N - 1; pos >= 0; --pos) {
int weapon = order[pos];
int limit = C[weapon];
for (int iter = 0; iter < limit; ++iter) {
int best_chest = -1;
int best_value = 1;
for (int nxt = pos + 1; nxt < N; ++nxt) {
int chest = order[nxt];
if (remaining[chest] <= 0) continue;
int val = remaining[chest];
int dmg = A[weapon][chest];
if (dmg < val) val = dmg;
if (val > best_value) {
best_value = val;
best_chest = chest;
}
}
if (best_chest == -1) break;
int dmg = min(A[weapon][best_chest], remaining[best_chest]);
if (dmg <= 1) break;
remaining[best_chest] -= dmg;
plan.weapon_use[weapon][best_chest] += 1;
++weapon_uses;
}
}
long long total = weapon_uses;
for (int chest = 0; chest < N; ++chest) {
if (remaining[chest] > 0) {
plan.bare_hand[chest] = remaining[chest];
total += remaining[chest];
}
}
plan.total_attacks = static_cast<int>(total);
return plan;
}
GraphState state_from_plan(const PlanDetail &plan) const {
GraphState state;
state.use = plan.weapon_use;
state.out_used.assign(N, 0);
for (int i = 0; i < N; ++i) {
int sum = 0;
for (int j = 0; j < N; ++j) { sum += state.use[i][j]; }
state.out_used[i] = sum;
}
return state;
}
bool recompute_ver15_state(Ver15State &st) const {
st.pos.assign(N, -1);
for (int idx = 0; idx < N; ++idx) {
int chest = st.order[idx];
st.pos[chest] = idx;
}
st.graph.out_used.assign(N, 0);
st.contribution.assign(N, 0);
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
int cnt = st.graph.use[i][j];
if (cnt == 0) continue;
if (st.pos[i] >= st.pos[j]) {
st.valid = false;
return false;
}
st.graph.out_used[i] += cnt;
if (st.graph.out_used[i] > C[i]) {
st.valid = false;
return false;
}
st.contribution[j] += 1LL * cnt * A[i][j];
}
}
st.bare_hand.assign(N, 0);
long long total = 0;
for (int i = 0; i < N; ++i) { total += st.graph.out_used[i]; }
for (int i = 0; i < N; ++i) {
long long need = static_cast<long long>(H[i]) - st.contribution[i];
if (need > 0) {
st.bare_hand[i] = static_cast<int>(need);
total += need;
}
}
st.score = total;
st.valid = true;
return true;
}
Ver15State build_ver15_state(const vector<int> &order, const PlanDetail &plan) const {
Ver15State st;
st.order = order;
st.graph.use = plan.weapon_use;
st.graph.out_used.assign(N, 0);
for (int i = 0; i < N; ++i) {
int sum = 0;
for (int j = 0; j < N; ++j) { sum += st.graph.use[i][j]; }
st.graph.out_used[i] = sum;
}
if (!recompute_ver15_state(st)) { st.valid = false; }
return st;
}
vector<long long> compute_contribution(const GraphState &state) const {
vector<long long> contribution(N, 0);
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
if (state.use[i][j] == 0) continue;
contribution[j] += 1LL * state.use[i][j] * A[i][j];
}
}
return contribution;
}
bool has_path(const GraphState &state, int src, int dst) const {
if (src == dst) return true;
vector<int> visited(N, 0);
vector<int> stack;
stack.reserve(N);
stack.push_back(src);
visited[src] = 1;
while (!stack.empty()) {
int v = stack.back();
stack.pop_back();
for (int to = 0; to < N; ++to) {
if (state.use[v][to] == 0) continue;
if (to == dst) return true;
if (!visited[to]) {
visited[to] = 1;
stack.push_back(to);
}
}
}
return false;
}
bool would_create_cycle(const GraphState &state, int from, int to) const {
if (from == to) return true;
return has_path(state, to, from);
}
StateEvaluation evaluate_state(const GraphState &state) const {
StateEvaluation eval;
vector<int> indeg(N, 0);
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
if (state.use[i][j] > 0) { indeg[j] += 1; }
}
}
vector<int> order;
order.reserve(N);
vector<int> queue;
queue.reserve(N);
for (int i = 0; i < N; ++i) {
if (indeg[i] == 0) queue.push_back(i);
}
for (size_t qi = 0; qi < queue.size(); ++qi) {
int v = queue[qi];
order.push_back(v);
for (int to = 0; to < N; ++to) {
if (state.use[v][to] > 0) {
if (--indeg[to] == 0) { queue.push_back(to); }
}
}
}
if (static_cast<int>(order.size()) != N) { return eval; }
vector<long long> contribution = compute_contribution(state);
vector<int> bare_hand(N, 0);
long long total = 0;
for (int i = 0; i < N; ++i) {
if (state.out_used[i] > C[i]) { return eval; }
total += state.out_used[i];
}
for (int i = 0; i < N; ++i) {
long long need = static_cast<long long>(H[i]) - contribution[i];
if (need > 0) {
bare_hand[i] = static_cast<int>(need);
total += need;
}
}
eval.valid = true;
eval.score = static_cast<int>(total);
eval.order = std::move(order);
eval.bare_hand = std::move(bare_hand);
return eval;
}
void remove_redundant_edges(GraphState &state) const {
vector<long long> contribution = compute_contribution(state);
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
while (state.use[i][j] > 0 && contribution[j] - A[i][j] >= H[j]) {
state.use[i][j] -= 1;
state.out_used[i] -= 1;
contribution[j] -= A[i][j];
}
}
}
}
bool apply_ver15_case_without_bare(Ver15State &st, int v) const {
int old_pos = st.pos[v];
vector<int> new_order = st.order;
int last_donor_pos = -1;
for (int w = 0; w < N; ++w) {
if (st.graph.use[w][v] > 0) { last_donor_pos = max(last_donor_pos, st.pos[w]); }
}
int insert_pos = last_donor_pos + 1;
new_order.erase(new_order.begin() + old_pos);
if (insert_pos > old_pos) insert_pos--;
if (insert_pos < 0) insert_pos = 0;
if (insert_pos > static_cast<int>(new_order.size())) insert_pos = static_cast<int>(new_order.size());
new_order.insert(new_order.begin() + insert_pos, v);
st.order = std::move(new_order);
for (int t = 0; t < N; ++t) { st.graph.use[v][t] = 0; }
if (!recompute_ver15_state(st)) return false;
vector<long long> contrib = st.contribution;
int pos_v = st.pos[v];
vector<int> targets;
targets.reserve(N);
for (int idx = pos_v + 1; idx < N; ++idx) {
int chest = st.order[idx];
if (contrib[chest] < H[chest]) { targets.push_back(chest); }
}
sort(targets.begin(), targets.end(), [&](int lhs, int rhs) {
if (A[v][lhs] != A[v][rhs]) return A[v][lhs] > A[v][rhs];
if (H[lhs] != H[rhs]) return H[lhs] < H[rhs];
return lhs < rhs;
});
int remaining = C[v];
for (int chest : targets) {
if (remaining <= 0) break;
int damage = A[v][chest];
if (damage <= 0) continue;
long long need = static_cast<long long>(H[chest]) - contrib[chest];
if (need <= 0) continue;
int add = static_cast<int>(min<long long>(remaining, (need + damage - 1) / damage));
if (add <= 0) continue;
st.graph.use[v][chest] += add;
contrib[chest] += 1LL * add * damage;
remaining -= add;
}
if (!recompute_ver15_state(st)) return false;
return true;
}
bool apply_ver15_case_with_bare(Ver15State &st, int v) const {
int old_pos = st.pos[v];
vector<int> new_order = st.order;
int last_target_pos = -1;
for (int t = 0; t < N; ++t) {
if (st.graph.use[v][t] > 0) { last_target_pos = max(last_target_pos, st.pos[t]); }
}
int insert_pos = (last_target_pos >= 0) ? last_target_pos : old_pos;
new_order.erase(new_order.begin() + old_pos);
if (insert_pos > old_pos) insert_pos--;
if (insert_pos < 0) insert_pos = 0;
if (insert_pos > static_cast<int>(new_order.size())) insert_pos = static_cast<int>(new_order.size());
new_order.insert(new_order.begin() + insert_pos, v);
st.order = std::move(new_order);
st.pos.assign(N, -1);
for (int idx = 0; idx < N; ++idx) { st.pos[st.order[idx]] = idx; }
int pos_v = st.pos[v];
for (int t = 0; t < N; ++t) {
if (st.graph.use[v][t] > 0 && st.pos[t] <= pos_v) { st.graph.use[v][t] = 0; }
}
for (int w = 0; w < N; ++w) { st.graph.use[w][v] = 0; }
if (!recompute_ver15_state(st)) return false;
vector<long long> contrib = st.contribution;
long long need = static_cast<long long>(H[v]) - contrib[v];
if (need <= 0) { return true; }
vector<int> candidates;
candidates.reserve(pos_v);
for (int idx = 0; idx < pos_v; ++idx) {
int weapon = st.order[idx];
if (A[weapon][v] <= 0) continue;
if (st.graph.out_used[weapon] >= C[weapon]) continue;
candidates.push_back(weapon);
}
sort(candidates.begin(), candidates.end(), [&](int lhs, int rhs) {
if (A[lhs][v] != A[rhs][v]) return A[lhs][v] > A[rhs][v];
if (C[lhs] != C[rhs]) return C[lhs] > C[rhs];
return lhs < rhs;
});
vector<long long> contrib_local = contrib;
for (int weapon : candidates) {
if (need <= 0) break;
int available = C[weapon] - st.graph.out_used[weapon];
if (available <= 0) continue;
int damage = A[weapon][v];
if (damage <= 0) continue;
long long required = (need + damage - 1) / damage;
int add = static_cast<int>(min<long long>(available, required));
if (add <= 0) continue;
st.graph.use[weapon][v] += add;
st.graph.out_used[weapon] += add;
contrib_local[v] += 1LL * add * damage;
need -= 1LL * add * damage;
}
if (!recompute_ver15_state(st)) return false;
return true;
}
pair<vector<int>, PlanDetail>
run_ver15_only(const vector<int> &order_v1, const PlanDetail &plan_v1, double time_limit_sec) {
if (time_limit_sec <= 0.0) { return {order_v1, plan_v1}; }
Ver15State current = build_ver15_state(order_v1, plan_v1);
if (!current.valid) { return {order_v1, plan_v1}; }
Ver15State best = current;
uniform_int_distribution<int> idx_dist(0, N - 1);
auto start_time = chrono::steady_clock::now();
while (true) {
auto now = chrono::steady_clock::now();
double elapsed = chrono::duration<double>(now - start_time).count();
if (elapsed > time_limit_sec) break;
int v = idx_dist(rng);
Ver15State candidate = current;
bool success = false;
if (current.bare_hand[v] == 0) {
success = apply_ver15_case_without_bare(candidate, v);
} else {
success = apply_ver15_case_with_bare(candidate, v);
}
if (!success || !candidate.valid) continue;
if (candidate.score < current.score) {
#ifdef HITONANODE_LOCAL
long long prev_score = current.score;
const char *op_name = (current.bare_hand[v] == 0) ? "ver1.5_after" : "ver1.5_before";
#endif
current = candidate;
if (candidate.score < best.score) { best = candidate; }
}
}
PlanDetail base_plan = plan_v1;
if (best.valid && best.score < plan_v1.total_attacks) {
PlanDetail result_plan;
result_plan.weapon_use = best.graph.use;
result_plan.bare_hand = best.bare_hand;
result_plan.total_attacks = static_cast<int>(best.score);
return {best.order, std::move(result_plan)};
}
return {order_v1, base_plan};
}
GraphState improve_state(GraphState initial_state, double time_limit_sec, StateEvaluation &best_eval_out) {
constexpr int DAMAGE_THRESHOLD = 5;
GraphState current = std::move(initial_state);
StateEvaluation current_eval = evaluate_state(current);
GraphState best_state = current;
StateEvaluation best_eval = current_eval;
uniform_int_distribution<int> idx_dist(0, N - 1);
auto start_time = chrono::steady_clock::now();
while (true) {
auto now = chrono::steady_clock::now();
double elapsed = chrono::duration<double>(now - start_time).count();
if (elapsed > time_limit_sec) break;
GraphState candidate = current;
vector<long long> contribution = compute_contribution(candidate);
int pivot = idx_dist(rng);
for (int j = 0; j < N; ++j) {
if (candidate.use[j][pivot] > 0) {
candidate.out_used[j] -= candidate.use[j][pivot];
contribution[pivot] -= 1LL * candidate.use[j][pivot] * A[j][pivot];
candidate.use[j][pivot] = 0;
}
}
for (int k = 0; k < N; ++k) {
if (candidate.use[pivot][k] > 0) {
candidate.out_used[pivot] -= candidate.use[pivot][k];
contribution[k] -= 1LL * candidate.use[pivot][k] * A[pivot][k];
candidate.use[pivot][k] = 0;
}
}
vector<int> donors;
donors.reserve(N);
for (int j = 0; j < N; ++j) {
if (j == pivot) continue;
if (A[j][pivot] <= DAMAGE_THRESHOLD) continue;
if (candidate.out_used[j] >= C[j]) continue;
donors.push_back(j);
}
sort(donors.begin(), donors.end(), [&](int lhs, int rhs) {
if (A[lhs][pivot] != A[rhs][pivot]) { return A[lhs][pivot] > A[rhs][pivot]; }
return H[lhs] < H[rhs];
});
long long remaining = static_cast<long long>(H[pivot]) - contribution[pivot];
for (int donor : donors) {
if (remaining <= 0) break;
int available = C[donor] - candidate.out_used[donor];
if (available <= 0) continue;
int damage = A[donor][pivot];
long long need = remaining > 0 ? remaining : 0;
long long use_count = min<long long>(available, (need + damage - 1) / damage);
if (use_count <= 0) continue;
candidate.use[donor][pivot] += static_cast<int>(use_count);
candidate.out_used[donor] += static_cast<int>(use_count);
contribution[pivot] += use_count * damage;
remaining = static_cast<long long>(H[pivot]) - contribution[pivot];
}
vector<int> targets;
targets.reserve(N);
for (int k = 0; k < N; ++k) {
if (k == pivot) continue;
if (A[pivot][k] <= DAMAGE_THRESHOLD) continue;
targets.push_back(k);
}
sort(targets.begin(), targets.end(), [&](int lhs, int rhs) {
if (A[pivot][lhs] != A[pivot][rhs]) { return A[pivot][lhs] > A[pivot][rhs]; }
return H[lhs] < H[rhs];
});
for (int target : targets) {
if (candidate.out_used[pivot] >= C[pivot]) break;
if (would_create_cycle(candidate, pivot, target)) continue;
long long need = static_cast<long long>(H[target]) - contribution[target];
if (need <= 0) continue;
int available = C[pivot] - candidate.out_used[pivot];
if (available <= 0) break;
int damage = A[pivot][target];
long long use_count = min<long long>(available, (need + damage - 1) / damage);
if (use_count <= 0) continue;
candidate.use[pivot][target] += static_cast<int>(use_count);
candidate.out_used[pivot] += static_cast<int>(use_count);
contribution[target] += use_count * damage;
}
StateEvaluation cand_eval = evaluate_state(candidate);
if (!cand_eval.valid) continue;
if (current_eval.valid && cand_eval.score >= current_eval.score) continue;
remove_redundant_edges(candidate);
cand_eval = evaluate_state(candidate);
if (!cand_eval.valid) continue;
if (current_eval.valid && cand_eval.score >= current_eval.score) continue;
#ifdef HITONANODE_LOCAL
#endif
current = std::move(candidate);
current_eval = cand_eval;
if (!best_eval.valid || cand_eval.score < best_eval.score) {
best_state = current;
best_eval = cand_eval;
}
}
if (best_eval.valid && (!best_eval_out.valid || best_eval.score < best_eval_out.score)) {
best_eval_out = best_eval;
}
return best_state;
}
pair<vector<int>, PlanDetail>
run_ver2_only(const vector<int> &order_v1, const PlanDetail &plan_v1, double time_limit_sec) {
PlanDetail base_plan = plan_v1;
base_plan.total_attacks = plan_v1.total_attacks;
if (time_limit_sec <= 0.0) { return {order_v1, base_plan}; }
GraphState initial_state = state_from_plan(plan_v1);
StateEvaluation initial_eval = evaluate_state(initial_state);
if (!initial_eval.valid) { return {order_v1, base_plan}; }
if (initial_eval.order.empty()) { initial_eval.order = order_v1; }
StateEvaluation best_eval = initial_eval;
GraphState best_state = initial_state;
StateEvaluation eval_out = best_eval;
GraphState improved = improve_state(initial_state, time_limit_sec, eval_out);
if (eval_out.valid && (!best_eval.valid || eval_out.score < best_eval.score)) {
best_state = std::move(improved);
best_eval = eval_out;
}
if (!best_eval.valid || best_eval.score >= plan_v1.total_attacks) { return {order_v1, base_plan}; }
PlanDetail result_plan;
result_plan.weapon_use = best_state.use;
result_plan.bare_hand = best_eval.bare_hand;
result_plan.total_attacks = best_eval.score;
return {best_eval.order, std::move(result_plan)};
}
vector<int> improve_order(vector<int> order, double time_limit_sec) {
vector<int> current = order;
int current_score = evaluate(current);
vector<int> best = current;
int best_score = current_score;
uniform_int_distribution<int> op_dist(0, 2);
uniform_int_distribution<int> idx_dist(0, N - 1);
auto start_time = chrono::steady_clock::now();
while (true) {
auto now = chrono::steady_clock::now();
double elapsed = chrono::duration<double>(now - start_time).count();
if (elapsed > time_limit_sec) break;
vector<int> candidate = current;
int op = op_dist(rng) * 0 + 1;
string op_name;
if (op == 0) { // swap
int i = idx_dist(rng);
int j = idx_dist(rng);
if (i == j) continue;
swap(candidate[i], candidate[j]);
op_name = "swap";
} else if (op == 1) { // insert
int i = idx_dist(rng);
int j = idx_dist(rng);
if (i == j) continue;
int val = candidate[i];
candidate.erase(candidate.begin() + i);
candidate.insert(candidate.begin() + j, val);
op_name = "insert";
} else { // reverse segment
int l = idx_dist(rng);
int r = idx_dist(rng);
if (l == r) continue;
if (l > r) swap(l, r);
reverse(candidate.begin() + l, candidate.begin() + r + 1);
op_name = "reverse";
}
int cand_score = evaluate(candidate);
if (cand_score < current_score) {
#ifdef HITONANODE_LOCAL
int prev_score = current_score;
#endif
current.swap(candidate);
current_score = cand_score;
#ifdef HITONANODE_LOCAL
#endif
if (cand_score < best_score) {
best = current;
best_score = cand_score;
}
}
}
return best;
}
void solve() {
vector<int> initial_order(N);
for (int i = 0; i < N; ++i) { initial_order[i] = i; }
sort(initial_order.begin(), initial_order.end(), [&](int lhs, int rhs) {
if (C[lhs] != C[rhs]) return C[lhs] > C[rhs];
if (H[lhs] != H[rhs]) return H[lhs] < H[rhs];
return lhs < rhs;
});
const double TOTAL_LIMIT = 1.8;
const double VER1_LIMIT = 0.6;
const double VER15_LIMIT = 0.6;
double VER2_LIMIT = TOTAL_LIMIT - VER1_LIMIT - VER15_LIMIT;
if (VER2_LIMIT < 0.0) VER2_LIMIT = 0.0;
vector<int> best_order_v1 = improve_order(initial_order, VER1_LIMIT);
PlanDetail best_plan_v1 = build_plan(best_order_v1);
vector<int> current_order = best_order_v1;
PlanDetail current_plan = best_plan_v1;
auto ver15_result = run_ver15_only(current_order, current_plan, VER15_LIMIT);
if (ver15_result.second.total_attacks < current_plan.total_attacks) {
current_order = ver15_result.first;
current_plan = ver15_result.second;
}
auto ver2_result = run_ver2_only(current_order, current_plan, VER2_LIMIT);
if (ver2_result.second.total_attacks < current_plan.total_attacks) {
current_order = ver2_result.first;
current_plan = ver2_result.second;
}
const vector<int> &final_order = current_order;
const PlanDetail &final_plan = current_plan;
ActionBuilder builder(H, A);
vector<pair<int, int>> actions = builder.build_actions(final_order, final_plan);
for (const auto &action : actions) { cout << action.first << ' ' << action.second << '\n'; }
}
};
using namespace std;
using lint = long long;
using pint = std::pair<int, int>;
using plint = std::pair<lint, lint>;
struct fast_ios {
fast_ios() {
std::cin.tie(nullptr), std::ios::sync_with_stdio(false), std::cout << std::fixed << std::setprecision(20);
};
} fast_ios_;
#include <cassert>
#include <limits>
// Rational number + {infinity(1 / 0), -infiity(-1 / 0), nan(0 / 0)} (有理数)
// Verified: Yandex Cup 2022 Final E https://contest.yandex.com/contest/42710/problems/K
template <class Int, bool AutoReduce = false> struct Rational {
Int num, den; // den >= 0
static constexpr Int my_gcd(Int a, Int b) {
// return __gcd(a, b);
if (a < 0) a = -a;
if (b < 0) b = -b;
while (a and b) {
if (a > b) {
a %= b;
} else {
b %= a;
}
}
return a + b;
}
constexpr Rational(Int num = 0, Int den = 1) : num(num), den(den) { normalize(); }
constexpr void normalize() noexcept {
if constexpr (AutoReduce) { // reduction
Int g = my_gcd(num, den);
if (g) num /= g, den /= g;
} else {
if (den == 0) {
if (num > 1) num = 1;
if (num < -1) num = -1;
}
}
if (den < 0) num = -num, den = -den; // denominator >= 0
}
constexpr bool is_finite() const noexcept { return den != 0; }
constexpr bool is_infinite_or_nan() const noexcept { return den == 0; }
constexpr Rational operator+(const Rational &r) const noexcept {
if (is_infinite_or_nan() and r.is_infinite_or_nan()) return Rational(num + r.num, 0);
return Rational(num * r.den + den * r.num, den * r.den);
}
constexpr Rational operator-(const Rational &r) const noexcept {
if (is_infinite_or_nan() and r.is_infinite_or_nan()) return Rational(num - r.num, 0);
return Rational(num * r.den - den * r.num, den * r.den);
}
constexpr Rational operator*(const Rational &r) const noexcept {
return Rational(num * r.num, den * r.den);
}
constexpr Rational operator/(const Rational &r) const noexcept {
return Rational(num * r.den, den * r.num);
}
constexpr Rational &operator+=(const Rational &r) noexcept { return *this = *this + r; }
constexpr Rational &operator-=(const Rational &r) noexcept { return *this = *this - r; }
constexpr Rational &operator*=(const Rational &r) noexcept { return *this = *this * r; }
constexpr Rational &operator/=(const Rational &r) noexcept { return *this = *this / r; }
constexpr Rational operator-() const noexcept { return Rational(-num, den); }
constexpr Rational abs() const noexcept { return Rational(num > 0 ? num : -num, den); }
constexpr Int floor() const {
assert(is_finite());
if (num > 0) {
return num / den;
} else {
return -((-num + den - 1) / den);
}
}
constexpr bool operator==(const Rational &r) const noexcept {
if (is_infinite_or_nan() or r.is_infinite_or_nan()) {
return num == r.num and den == r.den;
} else {
return num * r.den == r.num * den;
}
}
constexpr bool operator!=(const Rational &r) const noexcept { return !(*this == r); }
constexpr bool operator<(const Rational &r) const noexcept {
if (is_infinite_or_nan() and r.is_infinite_or_nan())
return num < r.num;
else if (is_infinite_or_nan()) {
return num < 0;
} else if (r.is_infinite_or_nan()) {
return r.num > 0;
} else {
return num * r.den < den * r.num;
}
}
constexpr bool operator<=(const Rational &r) const noexcept {
return (*this == r) or (*this < r);
}
constexpr bool operator>(const Rational &r) const noexcept { return r < *this; }
constexpr bool operator>=(const Rational &r) const noexcept {
return (r == *this) or (r < *this);
}
constexpr explicit operator double() const noexcept { return (double)num / (double)den; }
constexpr explicit operator long double() const noexcept {
return (long double)num / (long double)den;
}
template <class OStream> constexpr friend OStream &operator<<(OStream &os, const Rational &x) {
return os << x.num << '/' << x.den;
}
};
template <class Int> struct std::numeric_limits<Rational<Int, false>> {
static constexpr Rational<Int, false> max() noexcept {
return std::numeric_limits<Int>::max();
}
static constexpr Rational<Int, false> min() noexcept {
return std::numeric_limits<Int>::min();
}
static constexpr Rational<Int, false> lowest() noexcept {
return std::numeric_limits<Int>::lowest();
}
};
using Rat = Rational<lint>;
#include <cassert>
#include <queue>
#include <vector>
namespace linear_sum_assignment {
template <class T> struct Result {
T opt;
std::vector<int> mate;
std::vector<T> f, g; // dual variables
};
template <class T>
T augment(int nr, int nc, const std::vector<std::vector<T>> &C, std::vector<T> &f, std::vector<T> &g,
int s, // source row
std::vector<int> &mate,
std::vector<int> &mate_inv, // duplicates are allowed (used for k-best algorithms)
int fixed_rows = 0 // Ignore first rows and corresponding columns (used for k-best algorithms)
) {
assert(0 <= s and s < nr);
assert(mate.at(s) < 0);
static std::vector<T> dist;
static std::vector<int> prv;
dist.resize(nc);
prv.resize(nc);
std::vector<bool> done(nc);
for (int i = 0; i < fixed_rows; ++i) {
if (int j = mate.at(i); j >= 0) done.at(j) = 1;
}
{
int h = 0;
while (done.at(h)) ++h;
f.at(s) = C.at(s).at(h) - g.at(h);
for (int j = h + 1; j < nc; ++j) {
if (done.at(j)) continue;
f.at(s) = std::min(f.at(s), C.at(s).at(j) - g.at(j));
}
}
for (int j = 0; j < nc; ++j) {
if (!done.at(j)) {
dist.at(j) = C.at(s).at(j) - f.at(s) - g.at(j);
prv.at(j) = -1;
}
}
int t = -1;
std::vector<int> stk;
while (t == -1) {
int j1 = -1;
for (int j = 0; j < nc; ++j) {
if (done.at(j)) continue;
if (j1 == -1 or dist.at(j) < dist.at(j1) or
(dist.at(j) == dist.at(j1) and mate_inv.at(j) < 0)) {
j1 = j;
}
}
if (mate_inv.at(j1) < 0) {
t = j1;
break;
}
done.at(j1) = 1;
stk = {j1};
while (!stk.empty()) {
const int j2 = stk.back();
const int i = mate_inv.at(j2);
if (i < 0) {
t = stk.back();
break;
}
stk.pop_back();
for (int j = 0; j < nc; ++j) {
if (done.at(j)) continue;
const T len = C.at(i).at(j) - f.at(i) - g.at(j);
if (dist.at(j) > dist.at(j1) + len) {
dist.at(j) = dist.at(j1) + len;
prv.at(j) = j2;
}
if (len == T()) {
stk.push_back(j);
done.at(j) = 1;
}
}
}
}
const T len = dist.at(t);
f.at(s) += len;
for (int i = 0; i < fixed_rows; ++i) {
if (const int j = mate.at(i); j >= 0) done.at(j) = 0;
}
for (int j = 0; j < nc; ++j) {
if (!done.at(j)) continue;
g.at(j) -= len - dist.at(j);
}
for (int i = fixed_rows; i < nr; ++i) {
const int j = mate.at(i);
if (j < 0 or !done.at(j) or j >= nc) continue;
f.at(i) += len - dist.at(j);
}
T ret = T();
for (int cur = t; cur >= 0;) {
const int nxt = prv.at(cur);
if (nxt < 0) {
mate_inv.at(cur) = s;
mate.at(s) = cur;
ret += C.at(s).at(cur);
break;
}
const int i = mate_inv.at(nxt);
ret += C.at(i).at(cur) - C.at(i).at(nxt);
mate_inv.at(cur) = i;
mate.at(i) = cur;
cur = nxt;
}
return ret;
}
// Complexity: O(nr^2 nc)
template <class T> Result<T> _solve(int nr, int nc, const std::vector<std::vector<T>> &C) {
assert(nr <= nc);
std::vector<int> mate(nr, -1);
std::vector<int> mate_inv(nc, -1);
std::vector<T> f(nr), g(nc); // dual variables, f[i] + g[j] <= C[i][j] holds
if (nr == 0 or nc == 0) return {T(), mate, f, g};
if (nr == nc) {
// Column reduction
for (int j = nc - 1; j >= 0; --j) {
g.at(j) = C.at(0).at(j) - f.at(0);
int imin = 0;
for (int i = 1; i < nr; ++i) {
if (g.at(j) > C.at(i).at(j) - f.at(i)) {
imin = i;
g.at(j) = C.at(i).at(j) - f.at(i);
}
}
if (mate.at(imin) < 0) {
mate.at(imin) = j;
mate_inv.at(j) = imin;
} else if (g.at(j) < g.at(mate.at(imin))) {
mate_inv.at(mate.at(imin)) = -1;
mate.at(imin) = j;
mate_inv.at(j) = imin;
}
}
// Reduction transfer (can be omitted)
if (nc > 1) {
for (int i = 0; i < nr; ++i) {
if (mate.at(i) < 0) continue;
T best = C.at(i).at(0) - g.at(0), second_best = C.at(i).at(1) - g.at(1);
int argbest = 0;
if (best > second_best) std::swap(best, second_best), argbest = 1;
for (int j = 2; j < nc; ++j) {
if (T val = C.at(i).at(j) - g.at(j); val < best) {
second_best = best;
best = val;
argbest = j;
} else if (val < second_best) {
second_best = val;
}
}
g.at(argbest) -= second_best - best;
f.at(i) = second_best;
}
}
// Augmenting row reduction: not implemented
}
// Augmentation
for (int i = 0; i < nr; ++i) {
if (mate.at(i) < 0) augment(nr, nc, C, f, g, i, mate, mate_inv);
}
T ret = 0;
for (int i = 0; i < nr; ++i) ret += C.at(i).at(mate.at(i));
return {ret, mate, std::move(f), std::move(g)};
}
// Jonker–Volgenant algorithm: find minimum weight assignment
// Dual problem (nr == nc): maximize sum(f) + sum(g) s.t. f_i + g_j <= C_ij
// Complexity: O(nr nc min(nr, nc))
template <class T> Result<T> solve(int nr, int nc, const std::vector<std::vector<T>> &C) {
const bool transpose = (nr > nc);
if (!transpose) return _solve(nr, nc, C);
std::vector trans(nc, std::vector<T>(nr));
for (int i = 0; i < nr; ++i) {
for (int j = 0; j < nc; ++j) trans.at(j).at(i) = C.at(i).at(j);
}
auto [v, mate, f, g] = _solve(nc, nr, trans);
std::vector<int> mate2(nr, -1);
for (int j = 0; j < nc; ++j) {
if (mate.at(j) >= 0) mate2.at(mate.at(j)) = j;
}
return {v, mate2, g, f};
}
} // namespace linear_sum_assignment
template <class T> struct best_assignments {
struct Node {
T opt;
std::vector<int> mate;
std::vector<T> f, g; // dual variables
int fixed_rows;
std::vector<int> banned_js; // C[fixed_rows][j] が inf となる j の集合
// for priority queue
// NOTE: reverse order
bool operator<(const Node &rhs) const { return opt > rhs.opt; }
linear_sum_assignment::Result<T> to_output(bool transpose) const {
if (transpose) {
std::vector<int> mate2(g.size(), -1);
for (int i = 0; i < (int)mate.size(); ++i) mate2.at(mate.at(i)) = i;
return {opt, mate2, g, f};
} else {
return {opt, mate, f, g};
}
}
};
bool transpose;
int nr_, nc_;
T inf;
std::vector<std::vector<T>> C_, Ctmp_;
std::priority_queue<Node> pq;
best_assignments(int nr, int nc, const std::vector<std::vector<T>> &C, T inf)
: transpose(nr > nc), inf(inf) {
assert((int)C.size() == nr);
for (int i = 0; i < nr; ++i) assert((int)C.at(i).size() == nc);
nr_ = transpose ? nc : nr;
nc_ = transpose ? nr : nc;
C_.assign(nr_ + (nr_ != nc_), std::vector<T>(nc_, T()));
for (int i = 0; i < nr; ++i) {
for (int j = 0; j < nc; ++j) {
C_.at(transpose ? j : i).at(transpose ? i : j) = C.at(i).at(j);
}
}
Ctmp_ = C_;
auto [opt, mate, f, g] = linear_sum_assignment::solve(C_.size(), nc, C_);
pq.emplace(Node{opt, std::move(mate), std::move(f), std::move(g), 0, {}});
}
bool finished() const { return pq.empty(); }
linear_sum_assignment::Result<T> yield() {
assert(!pq.empty());
const Node ret = pq.top();
pq.pop();
for (int fixed_rows = ret.fixed_rows; fixed_rows < nr_; ++fixed_rows) {
std::vector<int> banned_js;
if (fixed_rows == ret.fixed_rows) banned_js = ret.banned_js;
const int s = fixed_rows;
banned_js.push_back(ret.mate.at(s));
if ((int)banned_js.size() >= nc_) continue;
auto f = ret.f;
auto g = ret.g;
auto mate = ret.mate;
std::vector<int> mate_inv(nc_, nr_);
for (int i = 0; i < nr_; ++i) mate_inv.at(mate.at(i)) = i;
std::vector<int> iscoldone(nc_);
for (int i = 0; i < fixed_rows; ++i) iscoldone.at(mate.at(i)) = 1;
for (int j : banned_js) Ctmp_.at(s).at(j) = inf;
mate_inv.at(mate.at(s)) = -1;
mate.at(s) = -1;
auto aug = linear_sum_assignment::augment(
nr_, nc_, Ctmp_, f, g, s, mate, mate_inv, fixed_rows);
for (int j = 0; j < nc_; ++j) {
if (mate_inv.at(j) < 0) { // nrows < ncols
g.at(j) = -f.back();
for (int i = fixed_rows; i < nr_; ++i) {
g.at(j) = std::min(g.at(j), Ctmp_.at(i).at(j) - f.at(i));
}
}
}
if (Ctmp_.at(s).at(mate.at(s)) < inf) {
pq.emplace(Node{
ret.opt + aug - C_.at(s).at(ret.mate.at(s)),
std::move(mate),
std::move(f),
std::move(g),
fixed_rows,
banned_js,
});
}
for (int j : banned_js) Ctmp_.at(s).at(j) = C_.at(s).at(j);
}
return ret.to_output(transpose);
}
};
#include <algorithm>
#include <numeric>
#include <utility>
#include <vector>
// UnionFind Tree (0-indexed), based on size of each disjoint set
struct UnionFind {
std::vector<int> par, cou;
UnionFind(int N = 0) : par(N), cou(N, 1) { iota(par.begin(), par.end(), 0); }
int find(int x) { return (par[x] == x) ? x : (par[x] = find(par[x])); }
bool unite(int x, int y) {
x = find(x), y = find(y);
if (x == y) return false;
if (cou[x] < cou[y]) std::swap(x, y);
par[y] = x, cou[x] += cou[y];
return true;
}
int count(int x) { return cou[find(x)]; }
bool same(int x, int y) { return find(x) == find(y); }
std::vector<std::vector<int>> groups() {
std::vector<std::vector<int>> ret(par.size());
for (int i = 0; i < int(par.size()); ++i) ret[find(i)].push_back(i);
ret.erase(std::remove_if(ret.begin(), ret.end(),
[&](const std::vector<int> &v) { return v.empty(); }),
ret.end());
return ret;
}
};
constexpr int N = 200;
// array<int, N> H, C;
vector<int> H, C;
vector<vector<int>> A;
// vector A(N, vector<int>(N));
// array<array<int, N>, N> A;
int EvaluateSubsequence(const vector<int> &seq) {
vector<pair<int, int>> yoryoku;
int ret = H.at(seq.front());
FOR(i, 1, seq.size()) {
const int u = seq.at(i - 1);
const int v = seq.at(i);
int r = H.at(v) - A.at(u).at(v) * C.at(u);
if (r > 0) {
sort(ALL(yoryoku), [&](const auto &lhs, const auto &rhs) {
const auto [lu, lr] = lhs;
const auto [ru, rr] = rhs;
return A.at(lu).at(v) < A.at(ru).at(v);
});
while (r > 0 and yoryoku.size()) {
auto [u, rr] = yoryoku.back();
const int d = A.at(u).at(v);
if (d < 30) break;
const int t = min(rr, (r + d - 1) / d);
r -= t * d;
yoryoku.back().second -= t;
if (yoryoku.back().second == 0) yoryoku.pop_back();
}
if (r > 0) ret += r;
} else {
const int t = (H.at(v) + A.at(u).at(v) - 1) / A.at(u).at(v);
const int rr = C.at(u) - t;
yoryoku.emplace_back(u, rr);
}
}
return ret;
}
#include <cstdint>
#include <vector>
uint32_t rand_int() // XorShift random integer generator
{
static uint32_t x = 123456789, y = 362436069, z = 521288629, w = 88675123;
uint32_t t = x ^ (x << 11);
x = y;
y = z;
z = w;
return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
}
double rand_double() { return (double)rand_int() / UINT32_MAX; }
template <class T> void shuffle_vec(std::vector<T> &vec) {
for (int i = 1; i < (int)vec.size(); ++i) {
const int j = rand_int() % (i + 1);
std::swap(vec.at(i), vec.at(j));
}
}
pair<vector<int>, PlanDetail> ImprovePlan(const vector<int> &order, const PlanDetail &plan, bool shuffle_order) {
vector<vector<int>> to(N);
vector<vector<int>> rev(N);
REP(i, N) {
REP(j, N) {
REP(_, plan.weapon_use.at(i).at(j)) {
to.at(i).push_back(j);
rev.at(j).push_back(i);
}
}
}
auto new_order = order;
if (shuffle_order) {
new_order.clear();
vector<int> in(N);
REP(i, N) {
for (int j : to.at(i)) in.at(j)++;
}
vector<int> vs;
REP(i, N) {
if (in.at(i) == 0) vs.push_back(i);
}
REP(_, N) {
const int idx = rand_int() % (int)vs.size();
const int v = vs.at(idx);
new_order.push_back(v);
swap(vs.at(idx), vs.back());
vs.pop_back();
for (int u : to.at(v)) {
in.at(u)--;
if (in.at(u) == 0) vs.push_back(u);
}
}
}
vector<int> erased_js;
REP(j, N) {
if (rev.at(j).empty()) continue;
if (plan.bare_hand.at(j) <= 10 or rand_int() % 2) {
const int idx = rand_int() % rev.at(j).size();
const int i = rev.at(j).at(idx);
to.at(i).erase(find(ALL(to.at(i)), j));
rev.at(j).erase(rev.at(j).begin() + idx);
erased_js.push_back(j);
}
}
vector<int> rem = H;
REP(i, N) {
for (int j : to.at(i)) {
rem.at(j) -= A.at(i).at(j);
}
}
for (auto &x : rem) chmax(x, 0);
vector<int> xs;
vector<int> head(N);
REP(i, N) {
REP(_, C.at(i) - (int)to.at(i).size()) {
head.at(i) = xs.size();
xs.push_back(i);
}
}
vector<int> invpos(N, -1);
REP(i, N) invpos.at(new_order.at(i)) = i;
vector cost(erased_js.size(), vector<lint>(xs.size()));
REP(r, cost.size()) REP(c, cost.at(0).size()) {
const int i = xs.at(c);
const int j = erased_js.at(r);
if (invpos.at(i) < invpos.at(j)) {
if (rem.at(j) > 0) {
const int u = min(rem.at(j), A.at(i).at(j));
cost.at(r).at(c) = -u;
}
}
}
auto [opt, mate, f, g] = linear_sum_assignment::solve(cost.size(), cost.at(0).size(), cost);
REP(r, cost.size()) {
const int j = erased_js.at(r);
const int c = mate.at(r);
if (c >= 0 and cost.at(r).at(c) < 0) {
const int i = xs.at(c);
to.at(i).push_back(j);
rem.at(j) -= A.at(i).at(j);
}
}
for (auto &x : rem) chmax(x, 0);
vector result(N, vector<int>(N));
REP(i, N) {
for (int j : to.at(i)) {
result.at(i).at(j)++;
}
}
PlanDetail ret;
ret.weapon_use = result;
ret.bare_hand = rem;
int total_attacks = 0;
for (int x : rem) total_attacks += x;
for (const auto &t : to) total_attacks += t.size();
// REP(i, N) REP(j, N) total_attacks += result.at(i).at(j);
ret.total_attacks = total_attacks;
if (total_attacks <= plan.total_attacks) {
return {new_order, ret};
} else {
return {order, plan};
}
// dbg(total_attacks);
// return {order, ret};
}
int main(int argc, char *argv[]) {
{
int N_;
cin >> N_;
H.resize(N_);
C.resize(N_);
A.resize(N_, vector<int>(N_));
cin >> H >> C >> A;
}
// vector<vector<pair<int, Rat>>> to(N);
// vector<vector<pair<int, Rat>>> rev(N);
// REP(i, N) REP(j, N) {
// if (i == j) continue;
// if (A.at(i).at(j) * C.at(i) >= H.at(j)) {
// const int t = (H.at(j) + A.at(i).at(j) - 1) / A.at(i).at(j);
// to.at(i).emplace_back(j, Rat{t, C.at(i)});
// rev.at(j).emplace_back(i, Rat{t, C.at(i)});
// }
// }
// REP(i, N) {
// dbg(make_tuple(i, to.at(i)));
// dbg(make_tuple(i, rev.at(i)));
// }
// REP(i, N) dbg(make_tuple(i, rev.at(i)));
vector cost(N, vector<lint>(N));
REP(i, N) REP(j, N) {
if (i == j) {
cost.at(i).at(j) = 1LL << 60;
} else {
const lint w = H.at(j) - A.at(i).at(j) * C.at(i);
if (w > 0) {
cost.at(i).at(j) = w;
}
}
}
Solver solver;
solver.N = N;
solver.H = H;
solver.C = C;
solver.A = A;
// best_assignments<lint> bsa(N, N, cost, 1LL << 60);
const auto [opt, mate, f, g] = linear_sum_assignment::solve(N, N, cost);
vector<int> state;
int state_eval = 1 << 30;
{
dbg(make_tuple(opt));
UnionFind uf(N);
REP(i, N) uf.unite(i, mate.at(i));
for (auto g : uf.groups()) dbg(g.size());
vector<int> visited(N);
vector<vector<int>> subsequences;
REP(i, N) {
if (!visited.at(i)) {
vector<int> seq;
vector<int> costs;
vector<int> hs;
for (int cur = i; !visited.at(cur); cur = mate.at(cur)) {
visited.at(cur) = 1;
seq.push_back(cur);
costs.push_back(cost.at(cur).at(mate.at(cur)));
hs.push_back(H.at(cur));
}
// dbg(seq);
// dbg(EvaluateSubsequence(seq));
vector<int> best_seq = seq;
int best_e = EvaluateSubsequence(seq);
REP(_, seq.size()) {
rotate(seq.begin(), seq.begin() + 1, seq.end());
const int e = EvaluateSubsequence(seq);
if (chmin(best_e, e)) { best_seq = seq; }
}
// dbg(best_e);
subsequences.push_back(best_seq);
// dbg(costs);
// dbg(hs);
}
}
jdump("num_subsequences", subsequences.size());
if (subsequences.size() <= 6) {
sort(ALL(subsequences));
do {
vector<int> cand_state;
for (auto s : subsequences) cand_state.insert(cand_state.end(), ALL(s));
// dbg(cand_state);
if (chmin(state_eval, solver.evaluate(cand_state))) { state = cand_state; }
} while (next_permutation(ALL(subsequences)));
} else {
sort(subsequences.begin(), subsequences.end(),
[&](const auto &lhs, const auto &rhs) { return lhs.size() > rhs.size(); });
vector<int> cand_state;
for (auto s : subsequences) cand_state.insert(cand_state.end(), ALL(s));
// dbg(cand_state);
if (chmin(state_eval, EvaluateSubsequence(cand_state))) { state = cand_state; }
}
}
// auto [opt, mate, f, g] = linear_sum_assignment::solve(N, N, cost);
// dbg(mate);
// dbg(opt);
// REP(i, N) dbg(make_tuple(i, cost.at(i).at(mate.at(i))));
vector<int> best_order = solver.improve_order(state, 0.8);
PlanDetail best_plan = solver.build_plan(best_order);
dbg(best_plan.total_attacks);
REP(_, 100) {
tie(best_order, best_plan) = ImprovePlan(best_order, best_plan, true);
}
dbg(best_plan.total_attacks);
REP(_, 6) {
tie(best_order, best_plan) = solver.run_ver2_only(best_order, best_plan, 0.1);
REP(_, 15) tie(best_order, best_plan) = ImprovePlan(best_order, best_plan, true);
dbg(best_plan.total_attacks);
}
// tie(best_order, best_plan) = solver.run_ver2_only(best_order, best_plan, 0.8);
// dbg(best_plan.total_attacks);
dbg(best_plan.total_attacks);
ActionBuilder builder(H, A);
const vector<pair<int, int>> actions = builder.build_actions(best_order, best_plan);
std::string solution;
for (const auto &action : actions) {
solution += std::to_string(action.first) + ' ' + std::to_string(action.second) + '\n';
// cout << action.first << ' ' << action.second << '\n';
}
solution.pop_back();
dump_onlinejudge(solution);
const int score = accumulate(ALL(H), 0) - (int)actions.size() + 1;
jdump("cost", actions.size());
jdump("score", score);
jdump("score150", score * 150);
dbg(actions.size());
dbg(score);
}
Submission Info
| Submission Time |
|
| Task |
A - Weakpoint |
| User |
hitonanode |
| Language |
C++ 23 (Clang 16.0.6) |
| Score |
8654454 |
| Code Size |
64069 Byte |
| Status |
AC |
| Exec Time |
1631 ms |
| Memory |
5568 KiB |
Compile Error
./Main.cpp:1668:14: warning: unused parameter 'argc' [-Wunused-parameter]
int main(int argc, char *argv[]) {
^
./Main.cpp:1668:26: warning: unused parameter 'argv' [-Wunused-parameter]
int main(int argc, char *argv[]) {
^
2 warnings generated.
Judge Result
| Set Name |
test_ALL |
| Score / Max Score |
8654454 / 15000000 |
| Status |
|
| Set Name |
Test Cases |
| test_ALL |
test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt |
| Case Name |
Status |
Exec Time |
Memory |
| test_0000.txt |
AC |
1570 ms |
5220 KiB |
| test_0001.txt |
AC |
1551 ms |
5328 KiB |
| test_0002.txt |
AC |
1572 ms |
5068 KiB |
| test_0003.txt |
AC |
1572 ms |
5344 KiB |
| test_0004.txt |
AC |
1627 ms |
5224 KiB |
| test_0005.txt |
AC |
1587 ms |
5424 KiB |
| test_0006.txt |
AC |
1558 ms |
5272 KiB |
| test_0007.txt |
AC |
1631 ms |
5356 KiB |
| test_0008.txt |
AC |
1595 ms |
5348 KiB |
| test_0009.txt |
AC |
1618 ms |
5340 KiB |
| test_0010.txt |
AC |
1552 ms |
5156 KiB |
| test_0011.txt |
AC |
1566 ms |
5236 KiB |
| test_0012.txt |
AC |
1626 ms |
5140 KiB |
| test_0013.txt |
AC |
1567 ms |
5204 KiB |
| test_0014.txt |
AC |
1558 ms |
5412 KiB |
| test_0015.txt |
AC |
1555 ms |
5460 KiB |
| test_0016.txt |
AC |
1559 ms |
5376 KiB |
| test_0017.txt |
AC |
1575 ms |
5308 KiB |
| test_0018.txt |
AC |
1555 ms |
5208 KiB |
| test_0019.txt |
AC |
1554 ms |
5308 KiB |
| test_0020.txt |
AC |
1585 ms |
5212 KiB |
| test_0021.txt |
AC |
1580 ms |
5472 KiB |
| test_0022.txt |
AC |
1569 ms |
5364 KiB |
| test_0023.txt |
AC |
1591 ms |
5392 KiB |
| test_0024.txt |
AC |
1566 ms |
5188 KiB |
| test_0025.txt |
AC |
1571 ms |
5272 KiB |
| test_0026.txt |
AC |
1627 ms |
5288 KiB |
| test_0027.txt |
AC |
1573 ms |
5284 KiB |
| test_0028.txt |
AC |
1565 ms |
5292 KiB |
| test_0029.txt |
AC |
1618 ms |
5168 KiB |
| test_0030.txt |
AC |
1578 ms |
5220 KiB |
| test_0031.txt |
AC |
1576 ms |
5416 KiB |
| test_0032.txt |
AC |
1559 ms |
5568 KiB |
| test_0033.txt |
AC |
1554 ms |
5392 KiB |
| test_0034.txt |
AC |
1612 ms |
5376 KiB |
| test_0035.txt |
AC |
1565 ms |
5328 KiB |
| test_0036.txt |
AC |
1573 ms |
5200 KiB |
| test_0037.txt |
AC |
1588 ms |
5268 KiB |
| test_0038.txt |
AC |
1616 ms |
5220 KiB |
| test_0039.txt |
AC |
1620 ms |
5548 KiB |
| test_0040.txt |
AC |
1574 ms |
5300 KiB |
| test_0041.txt |
AC |
1570 ms |
5344 KiB |
| test_0042.txt |
AC |
1558 ms |
5260 KiB |
| test_0043.txt |
AC |
1554 ms |
5272 KiB |
| test_0044.txt |
AC |
1573 ms |
5300 KiB |
| test_0045.txt |
AC |
1570 ms |
5300 KiB |
| test_0046.txt |
AC |
1580 ms |
5364 KiB |
| test_0047.txt |
AC |
1583 ms |
5204 KiB |
| test_0048.txt |
AC |
1562 ms |
5372 KiB |
| test_0049.txt |
AC |
1569 ms |
5424 KiB |
| test_0050.txt |
AC |
1568 ms |
5316 KiB |
| test_0051.txt |
AC |
1574 ms |
5228 KiB |
| test_0052.txt |
AC |
1577 ms |
5276 KiB |
| test_0053.txt |
AC |
1549 ms |
5200 KiB |
| test_0054.txt |
AC |
1591 ms |
5432 KiB |
| test_0055.txt |
AC |
1564 ms |
5352 KiB |
| test_0056.txt |
AC |
1570 ms |
5288 KiB |
| test_0057.txt |
AC |
1553 ms |
5220 KiB |
| test_0058.txt |
AC |
1548 ms |
5532 KiB |
| test_0059.txt |
AC |
1591 ms |
5328 KiB |
| test_0060.txt |
AC |
1555 ms |
5228 KiB |
| test_0061.txt |
AC |
1552 ms |
5144 KiB |
| test_0062.txt |
AC |
1554 ms |
5280 KiB |
| test_0063.txt |
AC |
1532 ms |
5064 KiB |
| test_0064.txt |
AC |
1556 ms |
5332 KiB |
| test_0065.txt |
AC |
1614 ms |
5232 KiB |
| test_0066.txt |
AC |
1584 ms |
5280 KiB |
| test_0067.txt |
AC |
1563 ms |
5276 KiB |
| test_0068.txt |
AC |
1623 ms |
5344 KiB |
| test_0069.txt |
AC |
1566 ms |
5196 KiB |
| test_0070.txt |
AC |
1591 ms |
5496 KiB |
| test_0071.txt |
AC |
1563 ms |
5236 KiB |
| test_0072.txt |
AC |
1554 ms |
5292 KiB |
| test_0073.txt |
AC |
1573 ms |
5356 KiB |
| test_0074.txt |
AC |
1563 ms |
5276 KiB |
| test_0075.txt |
AC |
1567 ms |
5396 KiB |
| test_0076.txt |
AC |
1549 ms |
5392 KiB |
| test_0077.txt |
AC |
1553 ms |
5396 KiB |
| test_0078.txt |
AC |
1558 ms |
5340 KiB |
| test_0079.txt |
AC |
1579 ms |
5228 KiB |
| test_0080.txt |
AC |
1586 ms |
5304 KiB |
| test_0081.txt |
AC |
1563 ms |
5344 KiB |
| test_0082.txt |
AC |
1589 ms |
5340 KiB |
| test_0083.txt |
AC |
1552 ms |
5324 KiB |
| test_0084.txt |
AC |
1571 ms |
5360 KiB |
| test_0085.txt |
AC |
1601 ms |
5388 KiB |
| test_0086.txt |
AC |
1570 ms |
5504 KiB |
| test_0087.txt |
AC |
1583 ms |
5196 KiB |
| test_0088.txt |
AC |
1562 ms |
5296 KiB |
| test_0089.txt |
AC |
1580 ms |
5172 KiB |
| test_0090.txt |
AC |
1577 ms |
5332 KiB |
| test_0091.txt |
AC |
1605 ms |
5264 KiB |
| test_0092.txt |
AC |
1584 ms |
5272 KiB |
| test_0093.txt |
AC |
1562 ms |
5296 KiB |
| test_0094.txt |
AC |
1549 ms |
5324 KiB |
| test_0095.txt |
AC |
1566 ms |
5328 KiB |
| test_0096.txt |
AC |
1575 ms |
5368 KiB |
| test_0097.txt |
AC |
1569 ms |
5228 KiB |
| test_0098.txt |
AC |
1612 ms |
5372 KiB |
| test_0099.txt |
AC |
1564 ms |
5304 KiB |
| test_0100.txt |
AC |
1566 ms |
5536 KiB |
| test_0101.txt |
AC |
1591 ms |
5356 KiB |
| test_0102.txt |
AC |
1566 ms |
5384 KiB |
| test_0103.txt |
AC |
1566 ms |
5388 KiB |
| test_0104.txt |
AC |
1580 ms |
5396 KiB |
| test_0105.txt |
AC |
1555 ms |
5308 KiB |
| test_0106.txt |
AC |
1543 ms |
5136 KiB |
| test_0107.txt |
AC |
1622 ms |
5344 KiB |
| test_0108.txt |
AC |
1581 ms |
5252 KiB |
| test_0109.txt |
AC |
1571 ms |
5364 KiB |
| test_0110.txt |
AC |
1601 ms |
5220 KiB |
| test_0111.txt |
AC |
1610 ms |
5400 KiB |
| test_0112.txt |
AC |
1590 ms |
5336 KiB |
| test_0113.txt |
AC |
1561 ms |
5436 KiB |
| test_0114.txt |
AC |
1576 ms |
5412 KiB |
| test_0115.txt |
AC |
1616 ms |
5316 KiB |
| test_0116.txt |
AC |
1579 ms |
5476 KiB |
| test_0117.txt |
AC |
1578 ms |
5400 KiB |
| test_0118.txt |
AC |
1572 ms |
5140 KiB |
| test_0119.txt |
AC |
1577 ms |
5428 KiB |
| test_0120.txt |
AC |
1571 ms |
5312 KiB |
| test_0121.txt |
AC |
1555 ms |
5224 KiB |
| test_0122.txt |
AC |
1562 ms |
5152 KiB |
| test_0123.txt |
AC |
1587 ms |
5160 KiB |
| test_0124.txt |
AC |
1560 ms |
5304 KiB |
| test_0125.txt |
AC |
1578 ms |
5404 KiB |
| test_0126.txt |
AC |
1584 ms |
5224 KiB |
| test_0127.txt |
AC |
1587 ms |
5236 KiB |
| test_0128.txt |
AC |
1609 ms |
5216 KiB |
| test_0129.txt |
AC |
1568 ms |
5372 KiB |
| test_0130.txt |
AC |
1605 ms |
5112 KiB |
| test_0131.txt |
AC |
1570 ms |
5272 KiB |
| test_0132.txt |
AC |
1553 ms |
5244 KiB |
| test_0133.txt |
AC |
1578 ms |
5428 KiB |
| test_0134.txt |
AC |
1607 ms |
5504 KiB |
| test_0135.txt |
AC |
1560 ms |
5208 KiB |
| test_0136.txt |
AC |
1567 ms |
5204 KiB |
| test_0137.txt |
AC |
1575 ms |
5532 KiB |
| test_0138.txt |
AC |
1619 ms |
5316 KiB |
| test_0139.txt |
AC |
1553 ms |
5264 KiB |
| test_0140.txt |
AC |
1564 ms |
5272 KiB |
| test_0141.txt |
AC |
1581 ms |
5272 KiB |
| test_0142.txt |
AC |
1605 ms |
5264 KiB |
| test_0143.txt |
AC |
1578 ms |
5488 KiB |
| test_0144.txt |
AC |
1585 ms |
5228 KiB |
| test_0145.txt |
AC |
1574 ms |
5348 KiB |
| test_0146.txt |
AC |
1594 ms |
5268 KiB |
| test_0147.txt |
AC |
1585 ms |
5372 KiB |
| test_0148.txt |
AC |
1560 ms |
5272 KiB |
| test_0149.txt |
AC |
1596 ms |
5536 KiB |