제출 #438643

#제출 시각아이디문제언어결과실행 시간메모리
438643fleimgruber던전 (IOI21_dungeons)C++17
63 / 100
7115 ms737016 KiB
// this solves subtask 1, 3, 4, 5. the complexity is alright, // but for larger n we get MLE. instead of binary lifting, we // should probably use some other base, HLD, those linear jump pointers.. #include "dungeons.h" #include <bits/stdc++.h> using namespace std; const int MAX_N = 50001; // subtasks 1, 3, 4, 5 const int MAX_LOG = 25; // 2^24 > 10^7 // 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[MAX_N][MAX_LOG][MAX_LOG]; int n, s[MAX_N], w[MAX_N]; // because p[i] = s[i], every time we lose, our strength doubles struct Subtask2 { struct { int end; long long gained; long long min; // min(sum(gained) - opponent) } dp[400001][MAX_LOG]; int l[400001]; Subtask2(vector<int>& l_) { copy_n(l_.begin(), n, l); l[n] = n; 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, int s_init) { long long strength = 0; 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 += s[at]; at = l[at]; } return strength; } }; optional<Subtask2> sub2; // don't waste memory :) 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(w_.begin(), n, w); w[n] = n; if (n > 50000) { sub2.emplace(l); return; } 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, int s_init) { if (n > 50000) return sub2->simulate(at, s_init); long long strength = s_init; 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 forward 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; }
#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...