#include <algorithm>
#include <chrono>
#include <cstdio>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <random>
#include <stack>
#include <utility>
#include <vector>
#include <fstream>
#include <set>
#include <cassert>
using std::cin;
using std::cout;
using std::endl;

using namespace std;

using lint=int64_t;
using uint=uint32_t;
using ulint=uint64_t;



class Timer {
  std::chrono::system_clock::time_point start_time_;

public:
  // restart the clock.
  void restart(void) {
    start_time_ = std::chrono::system_clock::now();
  }
  // When instantced, this timer restart itself automatically.
  Timer(void) {
    restart();
  }
  // Get the lap time(milliseconds).
  long long LapTime_millisec(void) {
    auto lap_time = std::chrono::system_clock::now();
    auto dur      = lap_time - start_time_;
    auto msec =
           std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
    return msec;
  }
  // Get the lap time(seconds).
  long long LapTime_sec(void) {
    auto lap_time = std::chrono::system_clock::now();
    auto dur      = lap_time - start_time_;
    auto sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
    return sec;
  }
};

class RandomIntNumber {
  std::mt19937_64 mt_maker_;
  std::uniform_int_distribution<int64_t> range_maker_;

public:
  RandomIntNumber(int64_t min, int64_t max) {
    std::random_device seed_maker;
    mt_maker_.seed(seed_maker());
    // range_maker_.min = min;
    // range_maker_.max = max;
    std::uniform_int_distribution<int64_t>::param_type param(min, max);
    range_maker_.param(param);
  }
  int64_t Make(void) {
    return range_maker_(mt_maker_);
  }
  int64_t operator()(void) {
    return Make();
  }
};

class RandomRealNumber {
  std::mt19937_64 mt_maker_;
  std::uniform_real_distribution<double> range_maker_;

public:
  RandomRealNumber(double min, double max) {
    std::random_device seed_maker;
    mt_maker_.seed(seed_maker());
    // range_maker_.min = min;
    // range_maker_.max = max;
    std::uniform_real_distribution<double>::param_type param(min, max);
    range_maker_.param(param);
  }

  double Make(void){
    return range_maker_(mt_maker_);
  }
  double operator()(void) {
    return Make();
  }
};

bool MoveToNext(const lint next_score,const lint curr_score,const double temperature,const bool maximize, RandomRealNumber& rand_real, bool should_move_better){
  lint delta=next_score-curr_score;
  if(!maximize){
    delta=-delta; // この処理をした時点で，deltaが正ならより良い結果
  }

  // cerr<<curr_score<<" "<<next_score<<" "<<delta<<" "<<"prob: "<<exp(delta/temperature)<<" "<<"temp: "<<temperature<<" ";

  if(delta>=0){
    // cerr<<"better"<<endl;
    return true;
  }else{
    if(should_move_better) return false;
    return exp(delta / temperature) > rand_real.Make();
  }

}

double GetTemperature(int curr_time_ms, int limit_time_ms, double temp_begin, double temp_end){
  double t=curr_time_ms/(double)limit_time_ms;
  return pow(temp_begin,1-t)*pow(temp_end,t);
}

struct Param{
    double temp_begin=  906382.760771487;
    double temp_end=36.76741356504612;
    //TODO: 必要があれば新たにパラメータを作成
    int swap_dist=19;
    int move_freq_begin=58;
    double move_freq_dec_exponent=4.200360923845264;
    double change_prob=0.6136755379299553;
  };



struct State{
  //TODO: 作成
  vector<int> type;
  vector<set<int>> date;
  State(int d):type(d+1),date(27){
    for(int i=1;i<=26;i++){
      date[i].insert(0);
      date[i].insert(d+1);
    }
  }
  /// \brief 初期状態のStateを作成
  void InitState(){
    //TODO: 実装
    RandomIntNumber randint(1,26);
    for(int i=1;i<type.size();i++){
      type[i]=randint();
      date[type[i]].insert(i);
    }
  }
  /// \brief 現在のstateからscoreを得る
  /// \return score
  lint CalcScore(vector<int>& c, vector<vector<int>>& s, int day){
    //TODO: 実装
    lint result=0;
    vector<int> last(27, 0);
    for(int d=1;d<=day;d++){
      result+=s[d][type[d]];
      last[type[d]]=d;
      for(int i=1;i<=26;i++){
        result-=c[i]*(d-last[i]);
      }
    }
    return result;
  }

  /// \brief curr_stateのtype[day]をqに変更した場合のスコアを得る
  lint UpdateScore(lint curr_score, vector<int>& c, vector<vector<int>>& s, int day, int q){
    if(type[day]==q){
      return curr_score;
    }
    auto itr_x=date[type[day]].find(day);
    auto itr_x_sml=itr_x;
    --itr_x_sml;
    auto itr_x_big=itr_x;
    ++itr_x_big;
    auto itr_y_big=date[q].upper_bound(day);
    auto itr_y_sml=itr_y_big;
    --itr_y_sml;
    int a=day-*(itr_y_sml);
    int b=day-*itr_x_sml;
    int e=*itr_x_big-day;
    int f=*itr_y_big-day;

    curr_score=curr_score
           -s[day][type[day]]
           +s[day][q]
           +(c[type[day]]*(b*(b-1)+e*(e-1))/2+c[q]*(a+f)*(a+f-1)/2)
           -(c[type[day]]*(b+e)*(b+e-1)/2 + c[q]*(a*(a-1)+f*(f-1))/2);

    return curr_score;
  }

  /// \brief type[day]をtoに変更．dateもそれに合わせて変える
  /// \param day
  /// \param to
  void Change(int day, int to){
    date[type[day]].erase(day);
    date[to].insert(day);
    type[day]=to;
  }

  bool operator==(const State& another)const{
    if(another.type.size() != type.size()){
      return false;
    }
    for(int i = 0; i < type.size(); i++){
      if(another.type[i] != type[i]){
        return false;
      }
    }
    return true;
  }
};

int main(int argc, char* argv[]){
  bool is_optimize=(argc>1);
  Timer timer;
  RandomRealNumber rand_real(0,1);


  RandomIntNumber rand_int_26(1,26);

  //TODO: コンテストに合わせて適当に調整
  /// \brief 何回のループごとに時間確認・冷却するか
  const int TIME_CHECK_FREQ=100;
  /// \brief 制限時間
  const int LIMIT_TIME_MS=2000;
  /// \brief 時間確認時に残り時間がこれ以下だったら終了
  const int TIME_MARGIN=20;
  /// \brief 最大化問題ならtrue
  const bool MAXIMIZE=true;

  Param param;
  //TODO: 標準入力からテストケースを受け取る
  int d;
  cin>>d;
  RandomIntNumber rand_int_d(1, d);
  vector<int>     c(27);
  vector<vector<int>> s(d+1, vector<int>(27));

  for(int i=1;i<=26;i++){
    cin>>c[i];
  }

  for(int i=1;i<=d;i++){
    for(int j=1;j<=26;j++){
      cin>>s[i][j];
    }
  }

  if(is_optimize){
    //TODO: パラメータをargvから取得
    param.temp_begin=stod(argv[1]);
    param.temp_end=stod(argv[2]);
    param.swap_dist = stoi(argv[3]);
    param.move_freq_begin=stoi(argv[4]);
    param.move_freq_dec_exponent=stod(argv[5]);
    param.change_prob=stod(argv[6]);
  }

  RandomIntNumber rand_int_swap_dist(-param.swap_dist, param.swap_dist);

  double temperature=param.temp_begin;

  int loop_cnt=0;
  State curr_state=State(d);
  curr_state.InitState();
  lint curr_score=curr_state.CalcScore(c,s,d);
  // cerr<<curr_score<<endl;

  lint laptime=0;
  int move_freq=param.move_freq_begin;

  bool should_move_better=false;

  while(true){
    loop_cnt++;
    if(loop_cnt%TIME_CHECK_FREQ==TIME_CHECK_FREQ-1){
      laptime=timer.LapTime_millisec();
      move_freq=pow((1-laptime/(double)LIMIT_TIME_MS), param.move_freq_dec_exponent)*param.move_freq_begin;
      move_freq=max(1, move_freq);
      // cerr<<move_freq<<endl;
      if(laptime>=LIMIT_TIME_MS-TIME_MARGIN){
        should_move_better=true;
        // if(is_optimize){
        //   cout<<curr_score<<endl;
        // }else{
        //   for(int i=1;i<=d;i++){
        //     cout<<curr_state.type[i]<<endl;
        //   }
        //   // cerr<<curr_score<<endl;
        // }
        // return 0;
      }else{
        temperature=GetTemperature(laptime,LIMIT_TIME_MS,param.temp_begin, param.temp_end);
        // cerr<<temperature<<endl;
      }
    }

    // TODO: これ以降のコードを実装
    // 現在の状態を保存

    // 少し変化させる

    vector<pair<int,int>> rev;
    lint next_score=curr_score;

    // State prev_state=curr_state;

    for(int i=0;i<move_freq;i++){
      if(rand_real.Make()<param.change_prob){
        // 1点更新
        int tgt=rand_int_d.Make();
        int to=rand_int_26.Make();
        rev.push_back(make_pair(tgt,curr_state.type[tgt]));

        next_score=curr_state.UpdateScore(next_score, c,s,tgt,to);
        curr_state.Change(tgt, to);
      }else{
        // swap
        int tgt_a=rand_int_d.Make();
        int tgt_b= rand_int_swap_dist.Make() + tgt_a;
        tgt_b=max(tgt_b,1);
        tgt_b=min(tgt_b,d);
        rev.push_back(make_pair(tgt_a,curr_state.type[tgt_a]));
        rev.push_back(make_pair(tgt_b, curr_state.type[tgt_b]));

        int mem=curr_state.type[tgt_a];
        next_score=curr_state.UpdateScore(next_score,c,s,tgt_a, curr_state.type[tgt_b]);
        curr_state.Change(tgt_a, curr_state.type[tgt_b]);
        next_score=curr_state.UpdateScore(next_score, c,s,tgt_b, mem);
        curr_state.Change(tgt_b,mem);

        // swap(curr_state.type[tgt_a], curr_state.type[tgt_b]);
      }
    }

    if(MoveToNext(next_score, curr_score, temperature, MAXIMIZE,rand_real, should_move_better)){
      //curr_stateを次の状態に変更
      curr_score=next_score;
    }else{
      if(should_move_better){

        if(is_optimize){
          cout<<curr_score<<endl;
        }else{
          for(int i=1;i<=d;i++){
            cout<<curr_state.type[i]<<endl;
          }
          // cerr<<curr_score<<endl;
        }
        return 0;
      }

      for(int i=rev.size()-1;i>=0;i--){
        curr_state.Change(rev[i].first, rev[i].second);
      }
      // cerr<<"reversed"<<endl;
      //curr_stateを保存した状態に戻す
      // curr_state.type[rev_a.first]=rev_a.second;

      // curr_state.Change(rev_a.first,rev_a.second);
      // if(rev_b.first!=-1){
      //   curr_state.Change(rev_b.first,rev_b.second);
      // }
      // assert(curr_state==prev_state);
    }

  }

}

Measured memory_bytes: 4392