제출 #438653

#제출 시각아이디문제언어결과실행 시간메모리
438653fleimgruber던전 (IOI21_dungeons)C++17
89 / 100
7076 ms738316 KiB
#include "dungeons.h" #include <bits/stdc++.h> using namespace std; const int MAX_N = 400001; const int MAX_LOG = 25; // 2^24 > 10^7 int n, s[MAX_N], p[MAX_N], w[MAX_N], l[MAX_N]; // MLE for other subtasks. probably requires smarter jump pointers struct Subtask1345 { // dp[i][j][k] = we're at i, simulate for 2^j steps and // win against an opponent if strength < 2^k (we don't gain strength) struct { int end; // where do we end up at? long long gained; // total strength gained on the path // max(sum(gained) - opponent) along the simulation, // for all opponents we lose against long long max; } dp[50001][MAX_LOG][MAX_LOG]; Subtask1345() { for (int j = 0; j < MAX_LOG; j++) for (int i = 0; i <= n; i++) for (int k = 0; k < MAX_LOG; k++) { auto& x = dp[i][j][k]; if (j == 0) if (i == n) { x.end = n; x.gained = x.max = 0; } else if (s[i] < (1 << k)) { // we win x.end = w[i]; x.gained = s[i]; x.max = LLONG_MIN; } else { x.end = l[i]; x.gained = p[i]; // 1 step behind (we didn't gain 'gained' yet) x.max = -s[i]; } else { auto& a = dp[i][j - 1][k]; auto& b = dp[a.end][j - 1][k]; x.end = b.end; x.gained = a.gained + b.gained; x.max = max(a.max, a.gained + b.max); } } } long long simulate(int at, long long strength) { int k = 0; auto advance_k = [&] { // msb if (strength >= (1 << 24)) k = 24; else while ((1 << (k + 1)) <= strength) k++; }; // find first time our dp takes the wrong turn. once this // happens, the msb will change => happens only log(something) // times at this vertex we win: // strength + gained - opponent >= 0 // => strength + dp.max >= 0 // but dp loses (dp.max is only calculated for losing edges) auto wrong_turn = [&](int i, int j, int k) { return strength + dp[i][j][k].max >= 0; }; while (at != n) { advance_k(); // jump to point where msb changes for (int j = MAX_LOG - 1; j >= 0; j--) if (!wrong_turn(at, j, k)) { // doesn't change => jump auto& x = dp[at][j][k]; at = x.end; strength += x.gained; } // else it changes, so don't jump forward // now wrong_turn(at, 0, k) holds, so we win strength += s[at]; at = w[at]; } return strength; } }; // because p[i] = s[i], every time we lose, our strength doubles // lots of copy-pasta struct Subtask2 { struct { int end; long long gained; long long min; // min(sum(gained) - opponent) } dp[MAX_N][MAX_LOG]; Subtask2() { for (int j = 0; j < MAX_LOG; j++) for (int i = 0; i <= n; i++) { auto& x = dp[i][j]; if (j == 0) if (i == n) { x.end = n; x.gained = x.min = 0; } else { x.end = w[i]; x.gained = s[i]; x.min = -s[i]; } else { auto& a = dp[i][j - 1]; auto& b = dp[a.end][j - 1]; x.end = b.end; x.gained = a.gained + b.gained; x.min = min(a.min, a.gained + b.min); } } } long long simulate(int at, long long strength) { auto lose = [&](int i, int j) { return strength + dp[i][j].min < 0; }; while (at != n) { // jump until we lose for (int j = MAX_LOG - 1; j >= 0; j--) if (!lose(at, j)) { auto& x = dp[at][j]; at = x.end; strength += x.gained; } // lose (if we're at the end, this adds 0) strength += p[at]; // = s[at] > strength at = l[at]; } return strength; } }; optional<Subtask1345> sub1345; optional<Subtask2> sub2; void init(int n_, vector<int> s_, vector<int> p_, vector<int> w_, vector<int> l_) { n = n_; copy_n(s_.begin(), n, s); copy_n(p_.begin(), n, p); copy_n(w_.begin(), n, w); copy_n(l_.begin(), n, l); w[n] = l[n] = n; if (n > 50000) sub2.emplace(); else sub1345.emplace(); } long long simulate(int at, int s_init) { return n > 50000 ? sub2->simulate(at, s_init) : sub1345->simulate(at, s_init); }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...