結果

問題 No.5003 物理好きクリッカー
ユーザー koyumeishikoyumeishi
提出日時 2018-12-04 14:00:03
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 7,894 ms / 10,000 ms
コード長 19,837 bytes
コンパイル時間 2,941 ms
実行使用メモリ 175,656 KB
スコア 253,351,608,192
平均クエリ数 10000.00
最終ジャッジ日時 2021-07-19 08:28:13
合計ジャッジ時間 258,699 ms
ジャッジサーバーID
(参考情報)
judge14 / judge11
純コード判定しない問題か言語
このコードへのチャレンジ(β)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 7,568 ms
171,800 KB
testcase_01 AC 7,612 ms
171,516 KB
testcase_02 AC 7,647 ms
172,324 KB
testcase_03 AC 7,845 ms
173,036 KB
testcase_04 AC 7,845 ms
174,944 KB
testcase_05 AC 7,730 ms
172,920 KB
testcase_06 AC 7,725 ms
172,944 KB
testcase_07 AC 7,829 ms
173,180 KB
testcase_08 AC 7,831 ms
174,712 KB
testcase_09 AC 7,694 ms
172,920 KB
testcase_10 AC 7,711 ms
172,128 KB
testcase_11 AC 7,846 ms
173,296 KB
testcase_12 AC 7,463 ms
171,124 KB
testcase_13 AC 7,763 ms
171,352 KB
testcase_14 AC 7,377 ms
170,728 KB
testcase_15 AC 7,643 ms
172,560 KB
testcase_16 AC 7,574 ms
172,564 KB
testcase_17 AC 7,645 ms
172,360 KB
testcase_18 AC 7,515 ms
172,684 KB
testcase_19 AC 7,709 ms
173,460 KB
testcase_20 AC 7,634 ms
173,044 KB
testcase_21 AC 7,701 ms
173,344 KB
testcase_22 AC 7,894 ms
172,368 KB
testcase_23 AC 7,738 ms
172,724 KB
testcase_24 AC 7,686 ms
173,464 KB
testcase_25 AC 7,617 ms
171,872 KB
testcase_26 AC 7,610 ms
172,932 KB
testcase_27 AC 7,571 ms
171,916 KB
testcase_28 AC 7,535 ms
172,240 KB
testcase_29 AC 7,464 ms
172,112 KB
testcase_30 AC 7,672 ms
172,188 KB
testcase_31 AC 7,695 ms
173,000 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#pragma GCC optimize ("-Ofast")

#include <iostream>
#include <vector>
#include <cstdio>
#include <sstream>
#include <map>
#include <string>
#include <algorithm>
#include <queue>
#include <cmath>
#include <functional>
#include <set>
#include <ctime>
#include <random>
#include <chrono>
#include <cassert>
#include <tuple>
#include <utility>
#include <memory>
#include <unordered_map>
#include <array>
#include <numeric>
using namespace std;

namespace {
  using Integer = long long; //__int128;
  template<class T, class S> istream& operator >> (istream& is, pair<T,S>& p){return is >> p.first >> p.second;}
  template<class T> istream& operator >> (istream& is, vector<T>& vec){for(T& val: vec) is >> val; return is;}
  template<class T> istream& operator ,  (istream& is, T& val){ return is >> val;}
  template<class T, class S> ostream& operator << (ostream& os, const pair<T,S>& p){return os << p.first << " " << p.second;}
  template<class T> ostream& operator << (ostream& os, const vector<T>& vec){for(size_t i=0; i<vec.size(); i++) os << vec[i] << (i==vec.size()-1?"":" "); return os;}
  template<class T> ostream& operator ,  (ostream& os, const T& val){ return os << " " << val;}

  template<class H> void print(const H& head){ cout << head; }
  template<class H, class ... T> void print(const H& head, const T& ... tail){ cout << head << " "; print(tail...); }
  template<class ... T> void println(const T& ... values){ print(values...); cout << endl; }

  template<class H> void eprint(const H& head){ cerr << head; }
  template<class H, class ... T> void eprint(const H& head, const T& ... tail){ cerr << head << " "; eprint(tail...); }
  template<class ... T> void eprintln(const T& ... values){ eprint(values...); cerr << endl; }

  class range{ Integer start_, end_, step_; public: struct range_iterator{ Integer val, step_; range_iterator(Integer v, Integer step) : val(v), step_(step) {} Integer operator * (){return val;} void operator ++ (){val += step_;} bool operator != (range_iterator& x){return step_ > 0 ? val < x.val : val > x.val;} }; range(Integer len) : start_(0), end_(len), step_(1) {} range(Integer start, Integer end) : start_(start), end_(end), step_(1) {} range(Integer start, Integer end, Integer step) : start_(start), end_(end), step_(step) {} range_iterator begin(){ return range_iterator(start_, step_); } range_iterator   end(){ return range_iterator(  end_, step_); } };

  inline string operator "" _s (const char* str, size_t size){ return move(string(str)); }
  constexpr Integer my_pow(Integer x, Integer k, Integer z=1){return k==0 ? z : k==1 ? z*x : (k&1) ? my_pow(x*x,k>>1,z*x) : my_pow(x*x,k>>1,z);}
  constexpr Integer my_pow_mod(Integer x, Integer k, Integer M, Integer z=1){return k==0 ? z%M : k==1 ? z*x%M : (k&1) ? my_pow_mod(x*x%M,k>>1,M,z*x%M) : my_pow_mod(x*x%M,k>>1,M,z);}
  constexpr unsigned long long operator "" _ten (unsigned long long value){ return my_pow(10,value); }

  inline int k_bit(Integer x, int k){return (x>>k)&1;} //0-indexed

  mt19937 mt(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count());

  template<class T> string join(const vector<T>& v, const string& sep){ stringstream ss; for(size_t i=0; i<v.size(); i++){ if(i>0) ss << sep; ss << v[i]; } return ss.str(); }

  inline string operator * (string s, int k){ string ret; while(k){ if(k&1) ret += s; s += s; k >>= 1; } return ret; }
}

namespace timer{
  using namespace chrono;
  steady_clock::time_point start_time;
  unsigned long long begin_time;
  unsigned long long int get_cycle()
  {
    unsigned int low, high;
    __asm__ volatile ("rdtsc" : "=a" (low), "=d" (high));
    return ((unsigned long long int)low) | ((unsigned long long int)high << 32);
  }

  void init(){
    start_time = steady_clock::now();
    begin_time = get_cycle();
  }

  double sec_per_cycle(){
    auto end_time = steady_clock::now();
    double elapsed = duration_cast<nanoseconds>(end_time - start_time).count() * 1e-9;
    return elapsed / (get_cycle() - begin_time);
  }

  // [sec]
  double get()
  {
    static double spc = sec_per_cycle();
    return (double)(get_cycle() - begin_time) * spc;
  }

};

class XorShift{
public:
  uint32_t x;
  uint32_t y;
  uint32_t z;
  uint32_t w;

  XorShift(){
    x = 123456789;
    y = 362436069;
    z = 521288629;
    w = 88675123;
  }
  
  XorShift(uint32_t seed){
    x = 123456789;
    y = 362436069;
    z = 521288629;
    w = seed;

    for(int i=0; i<100; i++){
      next();
    }
  }
  
  uint32_t next() {
    uint32_t t = x ^ (x << 11);
    x = y; y = z; z = w;
    return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); 
  }

  uint32_t operator () () {
    return next();
  }

  // [0, range)
  int operator () (int range){
    return (((long long)next()) * range) >> 32 ;
  }

  // [0, 1.0]
  double prob(){
    return (next()&0xfffffff) * 3.7252903123397e-9; // 0xfffffff = 268,435,455 = 2^28-1
  }

  template<class T>
  T& operator()(vector<T>& v){
    return v[ next()%v.size() ];
  }

  template<class T>
  void shuffle(vector<T>& v){
    int n = v.size();
    for(int i=0; i<n; i++){
      int j = (*this)(n-i) + i;
      swap(v[i], v[j]);
    }
  }
};

XorShift rng(114514);

using Int = int64_t; // __int128 ?

enum class Facility{
  CLICK = 0,
  HAND = 1,
  LILY = 2,
  FACTORY = 3,
  CASINO = 4,
  GRIMOIRE = 5,
  NONE = 6
};

enum class Command{
  CLICK = 0,
  BUY = 1,
  SELL = 2,
  REINFORCE = 3,
  ENHCLICK = 4,
  NOTHING = 5
};

constexpr int num_facilities = 6;
constexpr int num_commands = 6;

array<string, num_facilities> facility_name{
  "click"s,
  "hand"s,
  "lily"s,
  "factory"s,
  "casino"s,
  "grimoire"s
};

array<string, num_commands> command_name{
  "click"s,
  "buy"s,
  "sell"s,
  "reinforce"s,
  "enhclick"s,
  "nothing"s
};

string get_command_str(Command cmd, Facility target){
  if(target == Facility::NONE){
    return command_name[(int)cmd];
  }else{
    if(target == Facility::CLICK)
      return command_name[(int)Command::ENHCLICK];
    return command_name[(int)cmd] + " " + facility_name[(int)target];
  }
}

array<vector<Int>, num_facilities> buy_price{
  vector<Int>{-1},
  vector<Int>{150},
  vector<Int>{2000},
  vector<Int>{30000},
  vector<Int>{600000},
  vector<Int>{10000000}
};

array<vector<Int>, num_facilities> enh_price{
  vector<Int>{15},
  vector<Int>{1500},
  vector<Int>{20000},
  vector<Int>{300000},
  vector<Int>{6000000},
  vector<Int>{100000000}
};

array<vector<Int>, num_facilities> prod{
  vector<Int>{1},
  vector<Int>{1},
  vector<Int>{10},
  vector<Int>{120},
  vector<Int>{2000},
  vector<Int>{25000}
};

Int next_buy_price(Int p){
  // Int hoge = (p * 6 + 4) / 5;
  // Int res =  ceil(p*6.0 / 5.0);
  // assert(res == hoge);
  // return res;
  return (p * 6 + 4) / 5;
  // return p * 12 / 10 + !!(p * 12 % 10);
}

Int get_buy_price(Facility f, int cnt){
  if((int)buy_price[(int)f].size() <= cnt){
    buy_price[(int)f].emplace_back(next_buy_price(buy_price[(int)f].back()));
  }
  return buy_price[(int)f][cnt];
}

Int next_enh_price(Int p){
  return p * 10;
}

Int get_enh_price(Facility f, int level){
  if((int)enh_price[(int)f].size() <= level){
    enh_price[(int)f].emplace_back(next_enh_price(enh_price[(int)f].back()));
  }
  return enh_price[(int)f][level];
}

Int next_prod(Int p){
  return p * 2;
}

Int get_prod(Facility f, int level){
  if((int)prod[(int)f].size() <= level){
    prod[(int)f].emplace_back(next_prod(prod[(int)f].back()));
  }
  return prod[(int)f][level];
}

struct Movement{
  int turn;
  Command cmd;
  Facility target;
  // shared_ptr<Movement> prev;
  int prev;
};
struct State{
  Int cookie = 0;
  Int cookie_prod_per_turn = 0;
  Int future_cookies = 0;
  array<int, num_facilities> facility_cnt{};
  array<int, num_facilities> facility_level{};
  // shared_ptr<Movement> movement;
  int movement = -1;
  long long hash = 0;
};


constexpr int beam_width = 800;
// constexpr int beam_width = 100;
constexpr int num_next_state = 5*3 + 3;
constexpr int same_state_limt = 1;

template<size_t L, class T>
struct my_pool{
  array<T, L> pool;
  array<int, L> idx;
  int size = 0;
  my_pool(){
    iota(idx.begin(), idx.end(), 0);
  }
  int get(){
    return idx[size++];
  }
  void erase(int i){
    idx[--size] = i;
  }
  int set(const T& v){
    int i = get();
    pool[i] = v;
    return i;
  }
};

array<array<State, beam_width * num_next_state>, 3> beam_pool{};

array<Int, num_facilities> zhash_facility_cnt{};
array<Int, num_facilities> zhash_facility_level{};


int main(){
  timer::init();

  int n;
  cin >> n;
  string s;
  cin >> s;

  // init

  my_pool<10005 * beam_width + beam_width * num_next_state, Movement> movement_pool;

  s = "N" + s;
  vector<int> fever(s.size(), 0);
  for(int i=0; i<(int)s.size(); i++){
    if(s[i] == 'F'){
      fever[i] = 20;
    }
    if(i) fever[i] = max(fever[i], fever[i-1]-1);
  }

  for(int& i: fever){
    i = min(i, 1);
  }

  vector<int> fever_acc = fever;
  for(int i=1; i<(int)fever_acc.size(); i++){
    fever_acc[i] += fever_acc[i-1];
  }

  vector<int> next_bonus(s.size(), 0);
  for(int i=1; i<(int)s.size(); i++){
    if(s[i] == 'B'){
      next_bonus[i-1] = 1;
      // next_bonus[i] = 1;
    }
  }
  for(int i=s.size()-2; i>=0; i--){
    next_bonus[i] += next_bonus[i+1];
  }

  for(int i=0; i<num_facilities; i++){
    zhash_facility_level[i] = rng();
    zhash_facility_cnt[i] = rng();
  }

  int num_check_point = 12;
  int check_point_interval = n / num_check_point;

  vector<vector<long double>> gain_coeff(num_check_point, vector<long double>(s.size(), 0.0));
  for(int w=0; w<num_check_point; w++){
    int j = s.size()-1 - s.size() * w / num_check_point;
    for(int i=j-1; i>=0; i--){
      gain_coeff[w][i] = pow(1.01, next_bonus[i] - next_bonus[j]) * (fever[i]*6 + 1);
      gain_coeff[w][i] += gain_coeff[w][i + 1];
    }
  }

  auto calc_future_cookies = [&](State& state, int turn){
    turn++;
    static int check_point = 0;
    static int j = 0;

    static int i = -1;
    static double pk = 1;
    if(i != turn){
      i = turn;
      check_point = max<int>(0, num_check_point-2 - turn / check_point_interval);
      j = s.size() - 1 - s.size() * check_point / check_point_interval;
      pk = pow(1.01, next_bonus[turn] - next_bonus[j]);
    }

    Int res = ceil(
      state.cookie * pk +
      gain_coeff[check_point][turn] * (state.cookie_prod_per_turn + get_prod(Facility::CLICK, state.facility_level[(int)Facility::CLICK]))
      );

    // Int res = 0;
    // Int k = 1;
    // for(int c=0; c<num_check_point; c++){
    //   int j = s.size() - 1 - s.size() * c / check_point_interval;
    //   if(j < turn) break;
    //   res += ceil(
    //     state.cookie * pow(1.01, next_bonus[turn] - next_bonus[j]) +
    //     gain_coeff[c][turn] * (state.cookie_prod_per_turn + get_prod(Facility::CLICK, state.facility_level[(int)Facility::CLICK]))
    //     ) * k;
    //   k *= 10;
    // }
    return res;
  };


  // beam search

  vector<int> prev_beam_ids {0};
  beam_pool[0][0] = State{
  };
  beam_pool[0][0].facility_cnt[0] = 1;
  beam_pool[0][0].cookie = 0;
  beam_pool[0][0].cookie_prod_per_turn = 0;
  beam_pool[0][0].future_cookies = 0;
  beam_pool[0][0].hash = 0;

  for(int i=0; i<n; i++){
    int next_pool_size = 0;
    int next_pool_index = (i+1) % 3;

    // eprintln("turn=", i,
    //   ", cookies = ", beam_pool[i%3][prev_beam_ids[0]].cookie,
    //   ", future_cookies = ", beam_pool[i%3][prev_beam_ids[0]].future_cookies);
    // eprintln("beam_size =", prev_beam_ids.size());

    for(auto bid: prev_beam_ids){
      auto& state = beam_pool[i%3][bid];

      { // click (nothing)
        auto& next_state = beam_pool[next_pool_index][next_pool_size++];
        Int gain = get_prod(Facility::CLICK, state.facility_level[(int)Facility::CLICK]);
        if(fever[i]){
          gain *= 7;
        }
        next_state.cookie = state.cookie + gain;
        next_state.cookie_prod_per_turn = state.cookie_prod_per_turn;
        next_state.facility_cnt = state.facility_cnt;
        next_state.facility_level = state.facility_level;
        next_state.movement = movement_pool.set( Movement{
        // next_state.movement = make_shared<Movement>(Movement{
          i,
          Command::CLICK,
          Facility::NONE,
          state.movement
        });
        next_state.hash = state.hash;
      }
      { // buy
        for(int j=1; j<=5; j++){
          Int next_price = get_buy_price((Facility)j, state.facility_cnt[j]);
          if(s[i] == 'S'){
            // Int hoge = ceil(next_price * 0.9);
            // Int hoge = next_price = (next_price * 90) / 100 + !!(next_price * 90 % 100);
            next_price = (next_price * 90 + 99) / 100;
            // if(hoge != next_price){
            //   eprintln(hoge, next_price);
            // }
            // assert(hoge == next_price);
          }
          if(state.cookie < next_price) continue;

          auto& next_state = beam_pool[next_pool_index][next_pool_size++];
          next_state.cookie = state.cookie - next_price;
          next_state.cookie_prod_per_turn = state.cookie_prod_per_turn
            + get_prod((Facility)j, state.facility_level[j]);
          next_state.facility_cnt = state.facility_cnt;
          next_state.facility_cnt[j]++;
          next_state.facility_level = state.facility_level;
          next_state.movement = movement_pool.set( Movement{
          // next_state.movement = make_shared<Movement>(Movement{
            i,
            Command::BUY,
            (Facility)j,
            state.movement
          });
          next_state.hash = state.hash + zhash_facility_cnt[j];
        }
      }
      { // enhance
        for(int j=0; j<=5; j++){
          if(state.facility_cnt[j] == 0) continue;
          Int next_price = get_enh_price((Facility)j, state.facility_level[j]);
          if(s[i] == 'S'){
            // Int hoge = ceil(next_price * 0.9);
            next_price = (next_price * 90 + 99) / 100;
            // Int hoge = next_price = (next_price * 90) / 100 + !!(next_price * 90 % 100);
            // if(hoge != next_price){
            //   eprintln(hoge, next_price);
            // }
            // assert(hoge == next_price);
          }
          if(state.cookie < next_price) continue;
          Int diff = j == 0 ? 0 :
            get_prod((Facility)j, state.facility_level[j] + 1) -
            get_prod((Facility)j, state.facility_level[j]);

          auto& next_state = beam_pool[next_pool_index][next_pool_size++];
          next_state.cookie = state.cookie - next_price;
          next_state.cookie_prod_per_turn = state.cookie_prod_per_turn +
            diff * state.facility_cnt[j];
          next_state.facility_cnt = state.facility_cnt;
          next_state.facility_level = state.facility_level;
          next_state.facility_level[j]++;
          next_state.movement = movement_pool.set( Movement{
          // next_state.movement = make_shared<Movement>(Movement{
            i,
            Command::REINFORCE,
            (Facility)j,
            state.movement
          });
          next_state.hash = state.hash + zhash_facility_level[j];
        }
      }
      if( i > 9975 ){ // sell
        for(int j=1; j<=5; j++){
          if(state.facility_cnt[j] == 0) continue;
          Int sell_price = (get_buy_price((Facility)j, state.facility_cnt[j]-1) * 25 + 99) / 100;
          // Int hoge = ceil(get_buy_price((Facility)j, state.facility_cnt[j]-1) * 0.25);
          // assert(hoge == sell_price);

          auto& next_state = beam_pool[next_pool_index][next_pool_size++];
          next_state.cookie = state.cookie + sell_price;
          next_state.cookie_prod_per_turn = state.cookie_prod_per_turn -
            get_prod((Facility)j, state.facility_level[j]);
          next_state.facility_cnt = state.facility_cnt;
          next_state.facility_cnt[j]--;
          next_state.facility_level = state.facility_level;
          next_state.movement = movement_pool.set( Movement{
          // next_state.movement = make_shared<Movement>(Movement{
            i,
            Command::SELL,
            (Facility)j,
            state.movement
          });
          next_state.hash = state.hash - zhash_facility_cnt[j];
        }
      }

    }

    vector<pair<Int, int>> idx(next_pool_size);

    for(int b=0; b<next_pool_size; b++){
      auto& state = beam_pool[next_pool_index][b];
      Int gain = state.cookie_prod_per_turn;
      if(fever[i]){
        gain *= 7;
      }
      state.cookie += gain;

      if(s[i+1] == 'B'){
        // Int hoge = state.cookie + ceil(state.cookie * 0.01);

        state.cookie = (state.cookie * 101 + 99) / 100;
        // state.cookie += (state.cookie * 001 + 99) / 100;
        // assert(hoge == state.cookie);
      }

      state.future_cookies = calc_future_cookies(state, i);

      idx[b] = {-state.future_cookies, b};
    }

    int sz = min(next_pool_size, beam_width);
    // sort(idx.begin(), idx.end());

    vector<int> next_beam_ids;
    // map<Int, int> used;
    unordered_map<Int, int> used;

    // for(int j=0; j<(int)idx.size(); j++){
    //   auto& state = beam_pool[next_pool_index][idx[j].second];
    //   if(used[state.hash] == same_state_limt || (int)next_beam_ids.size() >= sz){
    //     movement_pool.erase(state.movement);
    //     continue;
    //   };
    //   next_beam_ids.emplace_back(idx[j].second);
    //   used[state.hash]++;
    // }

    int offset = 0;
    while(idx.size()){
      sz = min<int>(idx.size() - offset, beam_width - next_beam_ids.size());

      if(sz<=0){
        for(int j=0; j + offset<idx.size(); j++){
          auto& state = beam_pool[next_pool_index][idx[j + offset].second];
          movement_pool.erase(state.movement);
        }
        break;
      }

      nth_element(idx.begin() + offset, idx.begin()+sz + offset, idx.end());
      sort(idx.begin() + offset, idx.begin()+sz + offset);
      for(int j=0; j<sz; j++){
        auto& state = beam_pool[next_pool_index][idx[j+offset].second];
        if(used[state.hash] == same_state_limt){
          movement_pool.erase(state.movement);
          continue;
        };
        next_beam_ids.emplace_back(idx[j+offset].second);
        used[state.hash]++;
      }

      // idx = move(decltype(idx)(idx.begin()+sz, idx.end()));
      offset += sz;
    }

    prev_beam_ids = move(next_beam_ids);
  }

  {
    vector<pair<Int, int>> idx;
    auto& pool = beam_pool[n%3];

    for(auto bid: prev_beam_ids){
      auto& state = pool[bid];
      idx.emplace_back(-state.cookie, bid);
    }

    sort(idx.begin(), idx.end());

    auto& best_state = pool[idx.front().second];

    vector<pair<Command, Facility>> res;
    // shared_ptr<Movement> last_move = best_state.movement;
    Movement* last_move = &(movement_pool.pool[best_state.movement]);
    while(true){
      res.emplace_back(last_move -> cmd, last_move -> target);
      if(last_move -> turn == 0) break;
      // last_move = last_move -> prev;
      last_move = &(movement_pool.pool[last_move -> prev]);
    }

    assert(res.size() == n);

    eprintln("completd in ", timer::get()*1000, "[ms]");
    eprintln("score = ", best_state.cookie * 1e-9);

    reverse(res.begin(), res.end());

    for(int i=0; i<n; i++){
      #ifndef LOCAL_TEST
        println(get_command_str(res[i].first, res[i].second));
        string response;
        cin >> response;
        assert(response == "ok"s);
        // assert(response != "-1"s);
      #else
        // if(res[i].first != Command::CLICK){
        //   println("turn =", i, ", cmd = ", get_command_str(res[i].first, res[i].second));
        // }
      #endif
    }
  }

  // for(int i=0; i<num_facilities; i++){
  //   eprintln(facility_name[i]);
  //   eprintln("buy price:", buy_price[i]);
  //   eprintln("enh price:", enh_price[i]);
  //   eprintln("enh price:", prod[i]);
  // }

  return 0;
}
0