//#define _CRT_SECURE_NO_WARNINGS #include "bits/stdc++.h" #include #include #include #include #include #ifdef _MSC_VER //#include //#include //#include #include //#include #endif using namespace std; //呪文 #define DUMPOUT cerr #define dump(...) DUMPOUT<<" ";DUMPOUT<<#__VA_ARGS__<<" :["<<__LINE__<<":"<<__FUNCTION__<<"]"< pii; typedef pair pll; typedef pair pdd; typedef pair pss; template ostream& operator << (ostream& o, const pair<_KTy, _Ty>& m) { o << "{" << m.first << ", " << m.second << "}"; return o; } template ostream& operator << (ostream& o, const map<_KTy, _Ty>& m) { if (m.empty()) { o << "{ }"; return o; } o << "{" << *m.begin(); for (auto itr = ++m.begin(); itr != m.end(); itr++) { o << ", " << *itr; } o << "}"; return o; } template ostream& operator << (ostream& o, const unordered_map<_KTy, _Ty>& m) { if (m.empty()) { o << "{ }"; return o; } o << "{" << *m.begin(); for (auto itr = ++m.begin(); itr != m.end(); itr++) { o << ", " << *itr; } o << "}"; return o; } template ostream& operator << (ostream& o, const vector<_Ty>& v) { if (v.empty()) { o << "{ }"; return o; } o << "{" << v.front(); for (auto itr = ++v.begin(); itr != v.end(); itr++) { o << ", " << *itr; } o << "}"; return o; } template ostream& operator << (ostream& o, const set<_Ty>& s) { if (s.empty()) { o << "{ }"; return o; } o << "{" << *(s.begin()); for (auto itr = ++s.begin(); itr != s.end(); itr++) { o << ", " << *itr; } o << "}"; return o; } template ostream& operator << (ostream& o, const unordered_set<_Ty>& s) { if (s.empty()) { o << "{ }"; return o; } o << "{" << *(s.begin()); for (auto itr = ++s.begin(); itr != s.end(); itr++) { o << ", " << *itr; } o << "}"; return o; } template ostream& operator << (ostream& o, const stack<_Ty>& s) { if (s.empty()) { o << "{ }"; return o; } stack<_Ty> t(s); o << "{" << t.top(); t.pop(); while (!t.empty()) { o << ", " << t.top(); t.pop(); } o << "}"; return o; } template ostream& operator << (ostream& o, const list<_Ty>& l) { if (l.empty()) { o << "{ }"; return o; } o << "{" << l.front(); for (auto itr = ++l.begin(); itr != l.end(); ++itr) { o << ", " << *itr; } o << "}"; return o; } template istream& operator >> (istream& is, pair<_KTy, _Ty>& m) { is >> m.first >> m.second; return is; } template istream& operator >> (istream& is, vector<_Ty>& v) { for (size_t i = 0; i < v.size(); i++) is >> v[i]; return is; } namespace aux { // print tuple template struct tp { static void print(ostream& os, const Ty& v) { os << get(v) << ", "; tp::print(os, v); } }; template struct tp { static void print(ostream& os, const Ty& v) { os << get(v); } }; } template ostream& operator<<(ostream& os, const tuple& t) { os << "{"; aux::tp, 0, sizeof...(Tys) - 1>::print(os, t); os << "}"; return os; } template inline void Fill(A(&array)[N], const T &val) { std::fill((T*)array, (T*)(array + N), val); } void dump_func() { DUMPOUT << endl; } template void dump_func(Head&& head, Tail&&... tail) { DUMPOUT << head; if (sizeof...(Tail) == 0) { DUMPOUT << " "; } else { DUMPOUT << ", "; } dump_func(std::move(tail)...); } #define PI 3.14159265358979323846 #define EPS 1e-10 #define FOR(i,a,n) for(int i=(a);i<(n);++i) #define REP(i,n) FOR(i,0,n) #define all(j) (j).begin(), (j).end() #define SZ(j) ((int)(j).size()) #define fake false struct SXor128 { unsigned x, y, z, w; SXor128() { x = 123456789; y = 362436069; z = 521288629; w = 88675123; } SXor128(int _w) { x = 123456789; y = 362436069; z = 521288629; w = _w; } void setSeed() { x = 123456789; y = 362436069; z = 521288629; w = 88675123; } void setSeed(int _w) { x = 123456789; y = 362436069; z = 521288629; w = _w; } unsigned nextUInt() { unsigned t = (x ^ (x << 11)); x = y; y = z; z = w; return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))); } unsigned nextUInt(unsigned mod) { unsigned t = (x ^ (x << 11)); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); return w % mod; } unsigned nextUInt(unsigned l, unsigned r) { unsigned t = (x ^ (x << 11)); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); return w % (r - l + 1) + l; } double nextDouble() { return double(nextUInt()) / UINT_MAX; } } rnd; class timer { vector timers; int n = 0; public: #ifdef _MSC_VER double limit = 9.9; #else double limit = 9.9; #endif double t = 0; timer() {} timer(int size) : timers(size) {} bool elapses() const { return time() - t > limit; } void measure() { t = time() - t; ++n; } void measure(char id) { timers[id].measure(); } void print() { if (n % 2) measure(); for (int i = 0; i < 128; ++i) { if (timers[i].n) cerr << (char)i << ' ' << timers[i].t << 's' << endl; } cerr << " " << t << 's' << endl; } static double time() { #ifdef _MSC_VER return __rdtsc() / 2.6e9; #else unsigned long long a, d; __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); return (d << 32 | a) / 2.6e9; #endif } } timer(128); constexpr int N = 10000; //string S; const vector base_cost({ 150, 2000, 30000, 600000, 10000000 }); const vector base_prod({ 1, 10, 120, 2000, 25000 }); enum EAct { CLICK, BUY, SELL, REINFORCE, ENHCLICK, NOTHING }; const vector acts({ CLICK, BUY, REINFORCE, ENHCLICK, SELL }); const map dict_acts_se({ { "click", CLICK },{ "buy", BUY },{ "sell", SELL },{ "reinforce", REINFORCE },{ "enhclick", ENHCLICK },{ "nothing", NOTHING } }); const map dict_acts_es({ { CLICK, "click" } ,{ BUY, "buy" },{ SELL, "sell" },{ REINFORCE, "reinforce" },{ ENHCLICK, "enhclick" },{ NOTHING, "nothing" } }); enum EBld { HAND, LILY, FACTORY, CASINO, GRIMOIRE, NONE }; const vector blds({ HAND, LILY, FACTORY, CASINO, GRIMOIRE }); const map dict_blds_se({ { "hand", HAND },{ "lily", LILY },{ "factory", FACTORY },{ "casino", CASINO },{ "grimoire", GRIMOIRE },{ "none", NONE } }); const map dict_blds_es({ { HAND, "hand" },{ LILY,"lily" },{ FACTORY,"factory" },{ CASINO, "casino" },{ GRIMOIRE,"grimoire" },{ NONE,"none" } }); enum EEff { FEVER = 1, SALE = 2 }; class State { public: typedef shared_ptr StatePtr; shared_ptr S; // history : 解答出力用.あとで undo とか実装するかも array hist_act; array hist_bld; // ターン数 int turn; // effect のフラグ int eff_state; // effect の残りターン数 int eff_turn[3]; // 所持クッキー ll cookies; // cookies per click ll cpc; // 施設保有数 short num_blds[5]; // 基本生産性 ll prod[5]; // 購入コスト ll cost_buy[5]; // 施設強化コスト ll cost_rif[5]; // クリック強化コスト ll cost_enhclk; State() { turn = 0; cookies = 0; cpc = 1; eff_state = eff_turn[FEVER] = eff_turn[SALE] = 0; for (const auto& act : acts) { num_blds[act] = 0; prod[act] = base_prod[act]; cost_buy[act] = cost_rif[act] = base_cost[act]; cost_rif[act] *= 10; } cost_enhclk = 15; } // 行動.行動不可能(クッキー不足, 施設数 0 での売却)の場合 false が返る bool action(EAct act, EBld bld) { // 消費クッキーの計算 ll gains = 0, losts = 0, sells = 0; switch (act) { case CLICK: gains = cpc; break; case BUY: losts = cost_buy[bld]; break; case SELL: if (!num_blds[bld]) return false; // 売る施設がないよ sells = cost_buy[bld] * 5 / 6; // 現在の施設価格 sells = (sells + 3) / 4; // 25% break; case REINFORCE: losts = cost_rif[bld]; break; case ENHCLICK: losts = cost_enhclk; break; } ll diff = gains * ((eff_state & FEVER) ? 7 : 1) + sells - ((eff_state & SALE) ? (losts * 9 + 9) / 10 : losts); // クッキー不足 if (cookies + diff < 0) return false; // 各種 status の更新 switch (act) { case BUY: num_blds[bld]++; cost_buy[bld] = (cost_buy[bld] * 6 + 4) / 5; break; case SELL: num_blds[bld]--; cost_buy[bld] = cost_buy[bld] * 5 / 6; break; case REINFORCE: prod[bld] <<= 1; cost_rif[bld] *= 10; break; case ENHCLICK: cpc <<= 1; cost_enhclk *= 10; break; } // history に追加 hist_act[turn] = act; hist_bld[turn] = bld; // 行動によるクッキー総数の更新 cookies += diff; // 生産によるクッキー総数の更新 ll fever_const = (eff_state & FEVER) ? 7 : 1; for (const auto& bld : blds) cookies += num_blds[bld] * prod[bld] * fever_const; // 特殊効果のターン減少 eff_turn[FEVER]--; eff_turn[SALE]--; eff_state ^= eff_turn[FEVER] ? 0 : FEVER; eff_state ^= eff_turn[SALE] ? 0 : SALE; // 特殊効果の発動 switch (S->at(turn)) { case 'B': cookies = (cookies * 101 + 99) / 100; break; case 'F': eff_state |= FEVER; eff_turn[FEVER] = 20; break; case 'S': eff_state |= SALE; eff_turn[SALE] = 1; break; } // ターン数インクリメント turn++; return true; } // 巻き戻し void rewind(int t) { // 履歴以外を初期化 turn = 0; cookies = 0; cpc = 1; eff_state = eff_turn[FEVER] = eff_turn[SALE] = 0; for (const auto& act : acts) { num_blds[act] = 0; prod[act] = base_prod[act]; cost_buy[act] = cost_rif[act] = base_cost[act]; cost_rif[act] *= 10; } cost_enhclk = 15; // t ステップ進む for (int i = 0; i < t; i++) action(EAct(hist_act[i]), EBld(hist_bld[i])); } // 十分なクッキーが集まるまで click してからコマンドを実行 bool autoplay(EAct act, EBld bld, int turn_range = N) { int cur_turn = turn; while (!action(act, bld)) { action(CLICK, NONE); // turn_range ターン経過した場合は false if (turn >= min(N, cur_turn + turn_range)) return false; } return true; } // 最も高い施設を売却する bool sell_highest() { ll max_sell = INT64_MIN; EBld max_bld = NONE; for (auto bld : blds) { if (!num_blds[bld]) continue; ll sell = cost_buy[bld] * 5 / 6; sell = (sell + 3) / 4; if (max_sell < sell) { max_sell = sell; max_bld = bld; } } if (max_bld == NONE) return false; action(SELL, max_bld); return true; } // n ターンにわたって生産量の低い施設から売却していく bool sell_blds(int n) { vector candidates; // (prod, bld) ll cost_cpy[5], num_cpy[5]; REP(i, 5) { cost_cpy[i] = cost_buy[i]; num_cpy[i] = num_blds[i]; } for (int i = 0; i < n; i++) { ll max_sell = INT64_MIN; EBld max_bld = NONE; for (auto bld : blds) { if (!num_cpy[bld]) continue; ll sell = cost_cpy[bld] * 5 / 6; sell = (sell + 3) / 4; if (max_sell < sell) { max_sell = sell; max_bld = bld; } } if (max_bld == NONE) return false; candidates.emplace_back(prod[max_bld], max_bld); cost_cpy[max_bld] = cost_cpy[max_bld] * 5 / 6; num_cpy[max_bld]--; } sort(all(candidates)); for (auto& e : candidates) action(SELL, EBld(e.second)); return true; } // 効率のいい ( Log(1+ΔCpT/CpT)/T の最も大きな ) ものを貪欲に実行する // https://ita.hatenadiary.jp/entry/20130920/p1 // undo があるといいんだけど bool greedy() { ll cur_turn = turn; ll cur_prod_sum = 0; for (auto bld : blds) cur_prod_sum += num_blds[bld] * prod[bld]; cur_prod_sum += cpc; double best_score = DBL_MIN; EAct best_act; EBld best_bld; for (auto act : { BUY, REINFORCE, ENHCLICK }) { auto bs = (act == ENHCLICK ? vector({ NONE }) : blds); for (auto bld : blds) { State next_state(*this); if (!next_state.autoplay(act, bld)) continue; ll next_turn = next_state.turn; ll next_prod_sum = 0; for (auto b : blds) next_prod_sum += next_state.num_blds[b] * next_state.prod[b]; next_prod_sum += cpc; double score = log(1 + double(next_prod_sum - cur_prod_sum) / next_prod_sum) / (next_turn - cur_turn); if (score > best_score) { best_score = score; best_act = act; best_bld = bld; } } } if (best_score == DBL_MIN) return false; autoplay(best_act, best_bld); return true; } ll prod_sum() { ll res = 0; for (auto bld : blds) res += prod[bld] * num_blds[bld]; res += cpc; return res; } void show_log(ostream& os) { map eff_cnt; for (auto s : *S) { if (s != 'N') { eff_cnt[s]++; os << s; } } os << endl; os << eff_cnt << endl; for (int i = 0; i < turn; i++) { EAct act = EAct(hist_act[i]); EBld bld = EBld(hist_bld[i]); if (act == CLICK) continue; os << dict_acts_es.at(act); if (bld != NONE) os << " " << dict_blds_es.at(bld); os << endl; } } }; typedef shared_ptr StatePtr; StatePtr solve(const StatePtr& init_state) { init_state->autoplay(ENHCLICK, NONE); init_state->autoplay(ENHCLICK, NONE); ll best_cookies = init_state->cookies; StatePtr best_state = init_state; SXor128 r(init_state->cookies % 1000000007); StatePtr state(new State(*init_state)); map turns_prods; while (state->turn < N) { state->greedy(); ll prods = 0; REP(i, 5) prods += state->prod[i] * state->num_blds[i]; prods += state->cpc; turns_prods[state->turn] = prods; } if (state->turn != N) cerr << state->turn << endl; // 0 ターンまで巻き戻し state->rewind(0); for (int i = 0; i < N; i++) { state->action(EAct(state->hist_act[i]), EBld(state->hist_bld[i])); // 5000 ターン以降のキーフレーム if (state->turn >= 5000 && turns_prods.find(state->turn) != turns_prods.end()) { // 全部クリック for (int i = 40; i >= 0; i--) { StatePtr s(new State(*state)); for (int t = s->turn; t < N - i; t++) s->action(CLICK, NONE); //cerr << "before sell: " << s->cookies << ", "; //for (int t = s->turn; t < N; t++) s->sell_highest(); if (!s->sell_blds(i)) cerr << "error!" << endl; //cerr << "after sell: " << s->cookies << endl; //cerr << state->turn << ": " << s->cookies << endl; if (best_cookies < s->cookies) { best_cookies = s->cookies; best_state = s; } } } } //cerr << loopcnt << endl; return best_state; } //State BeamSearch(State FirstState) { // Heap HNowStates = new Heap(); // HNowState.push(FirstState) : // int k = 100; // ビーム幅 // for (size_t t = 0; t < MaxTurn; t++) { // Heap HNextStates = new Heap(); // for (size_t i = 0; i < k; i++) { // if (HNowStates.top == null) break; // var NowState = HNowStates.pop(); // foreach(var NextState in NowState.GetAllNextState()) { // HNextStates.push(NextState); // } // } // HNowStates = HNextStates; // } // var BestState = HNowStates.pop(); // return BestState; //} typedef pair Obj; bool operator < (const Obj& a, const Obj& b) { return a.first == b.first ? a.second->prod_sum() > b.second->prod_sum() : a.first < b.first; } bool operator < (const StatePtr& a, const StatePtr& b) { return a->prod_sum() > b->prod_sum(); } vector getAllNextState(const Obj& obj) { vector res; const auto& state = obj.second; ll cur_turn = state->turn; ll cur_prod_sum = state->prod_sum(); for (auto act : { BUY, REINFORCE, ENHCLICK }) { auto bs = (act == ENHCLICK ? vector({ NONE }) : blds); for (auto bld : blds) { StatePtr next_state(new State(*state)); if (!next_state->autoplay(act, bld)) continue; ll next_turn = next_state->turn; ll next_prod_sum = next_state->prod_sum(); double score = log(1.0 + double(next_prod_sum - cur_prod_sum) / next_prod_sum) / (next_turn - cur_turn); res.emplace_back(score, next_state); } } return res; } StatePtr beam_search(const StatePtr& init_state) { priority_queue best_states; StatePtr state(new State(*init_state)); priority_queue now_states; now_states.emplace(0.0, state); ll max_prod = 0; int width = 1; int loopcnt = 0; while (!now_states.empty()) { priority_queue next_states; for (int i = 0; i < width; i++) { if (now_states.empty()) break; //set scores; auto now_state = now_states.top(); now_states.pop(); for (auto next_state : getAllNextState(now_state)) { //if (scores.find(next_state.first) != scores.end()) continue; //scores.insert(next_state.first); next_states.push(next_state); } } now_states = next_states; if (now_states.empty()) break; //cerr << "loopcnt: " << loopcnt << endl; while (!next_states.empty()) { auto s = next_states.top(); next_states.pop(); //cerr << s.second->turn << ": " << s.second->prod_sum() << " " << s.first << endl; if (s.second->prod_sum() > max_prod) { best_states.push(s.second); if (best_states.size() >= 10) best_states.pop(); max_prod = s.second->prod_sum(); //cerr << s.second->turn << ": " << max_prod << endl; } } auto top = now_states.top().second; //cerr << top->turn << ": " << top->prod_sum() << endl; loopcnt++; } //cerr << best_states.size() << endl; StatePtr best_state = solve(init_state); ll best_cookies = best_state->cookies; while (!best_states.empty()) { auto state = best_states.top(); best_states.pop(); ll max_turn = state->turn; set turns_prods; for (int i = 0; i < state->turn; i++) { if (state->hist_act[i] != CLICK) { turns_prods.insert(i); } } // 0 ターンまで巻き戻し state->rewind(0); for (int i = 0; i < max_turn; i++) { state->action(EAct(state->hist_act[i]), EBld(state->hist_bld[i])); // 5000 ターン以降のキーフレーム if (state->turn >= 5000 && turns_prods.find(state->turn) != turns_prods.end()) { // 全部クリック for (int i = 40; i >= 0; i--) { StatePtr s(new State(*state)); for (int t = s->turn; t < N - i; t++) s->action(CLICK, NONE); //cerr << "before sell: " << s->cookies << ", "; //for (int t = s->turn; t < N; t++) s->sell_highest(); if (!s->sell_blds(i)) cerr << "error!" << endl; // if (!s->sell_highest()) cerr << "error!" << endl; //cerr << "after sell: " << s->cookies << endl; //cerr << state->turn << ": " << s->cookies << endl; if (best_cookies < s->cookies) { best_cookies = s->cookies; //cerr << best_cookies << endl; best_state = s; } } } } } return best_state; } string testcase(unsigned seed) { SXor128 r(seed); REP(i, 100) r.nextUInt(); int range_min = 0, range_max = 199; string S(N, 'N'); while (range_min < N) { int idx = r.nextUInt(range_min, range_max); if (idx >= N) break; int kind = r.nextUInt(3); if (kind == 0) S[idx] = 'B'; else if (kind == 1) S[idx] = 'F'; else S[idx] = 'S'; range_min = idx + 100; range_max = idx + 200; } return S; } //#ifdef _MSC_VER //typedef struct SMouseParams { // int event, x, y, flags; // SMouseParams() {} // SMouseParams(int event, int x, int y, int flags) : event(event), x(x), y(y), flags(flags) {} //}*SMouseParamsPtr; // //void mouse_callback(int event, int x, int y, int flags, void* params) { // SMouseParamsPtr mp = static_cast(params); // mp->event = event; // mp->x = x; // mp->y = y; // mp->flags = flags; //} // //struct Button { // //int x1, y1, x2, y2; // cv::Rect roi; // EAct act; // EBld bld; // string text; // cv::Mat_ img; // // Button(int x1, int y1, int x2, int y2, EAct act, EBld bld, string text) // : roi(x1, y1, x2 - x1, y2 - y1), act(act), bld(bld), text(text) // { // img = cv::Mat_(y2 - y1, x2 - x1, cv::Vec3b(255, 255, 255)); // cv::rectangle(img, cv::Rect(0, 0, x2 - x1, y2 - y1), cv::Scalar(0, 0, 0), 3, cv::LINE_AA); // cv::putText(img, text, cv::Point(20, y2 - y1 - 32), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1, cv::LINE_AA); // //ll diff = state->diff(act, bld); // //cv::putText(img, to_string(abs(diff)), cv::Point(20, y2 - y1 - 8), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1, cv::LINE_AA); // } //}; // //void play(StatePtr state) { // // int W = 500; // int H = 500; // cv::Mat_ board(H, W, cv::Vec3b(255, 255, 255)); // // vector