#![allow(non_snake_case, unused)]

use proconio::input;
use rand::prelude::*;
use rand_pcg::Mcg128Xsl64;
use std::fs::File;
use std::io::prelude::*;

// utility --------------------------------------------------

trait IndexAt<T> {
    fn at(&self, i: i32) -> T;
    fn mt(&mut self, i: i32) -> &mut T;
}

trait IndexAt2<T> {
    fn at(&self, i1: i32, i2: i32) -> T;
    fn mt(&mut self, i1: i32, i2: i32) -> &mut T;
}

impl<T: Copy> IndexAt<T> for Vec<T> {
    fn at(&self, i: i32) -> T {
        self[i as usize]
    }
    fn mt(&mut self, i: i32) -> &mut T {
        &mut self[i as usize]
    }
}

impl<T: Copy> IndexAt2<T> for Vec<Vec<T>> {
    fn at(&self, i1: i32, i2: i32) -> T {
        self[i1 as usize][i2 as usize]
    }
    fn mt(&mut self, i1: i32, i2: i32) -> &mut T {
        &mut self[i1 as usize][i2 as usize]
    }
}

pub fn get_time() -> f64 {
    // TODO: structで明示的に処理したい
    static mut START_TIME: f64 = -1.0;
    let t = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap();
    let ms = t.as_secs() as f64 + t.subsec_nanos() as f64 * 1e-9;
    unsafe {
        if START_TIME < 0.0 {
            START_TIME = ms;
        }
        ms - START_TIME
    }
}

#[cfg(feature = "local")]
use log::{debug, error, info, warn, LevelFilter};

struct Logger {}

#[cfg(feature = "local")]
impl Logger {
    fn init() -> () {
        let log_path = "log/log_main.log";
        use std::fs;
        use std::path::Path;
        if Path::new(log_path).exists() {
            fs::remove_file(log_path).unwrap_or(());
        }
        use log::LevelFilter;
        use log4rs::append::console::ConsoleAppender;
        use log4rs::append::file::FileAppender;
        use log4rs::config::{Appender, Config, Logger, Root};
        use log4rs::encode::pattern::PatternEncoder;
        use log4rs::filter::threshold::ThresholdFilter;

        let stdout = ConsoleAppender::builder().build();
        let fileout = FileAppender::builder()
            .encoder(Box::new(PatternEncoder::new("{d} - {m}{n}")))
            .build(log_path)
            .unwrap();
        let config = Config::builder()
            .appender(
                Appender::builder()
                    .filter(Box::new(ThresholdFilter::new(log::LevelFilter::Info)))
                    .build("stdout", Box::new(stdout)),
            )
            .appender(
                Appender::builder()
                    .filter(Box::new(ThresholdFilter::new(log::LevelFilter::Debug)))
                    .build("fileout", Box::new(fileout)),
            )
            .build(
                Root::builder()
                    .appender("stdout")
                    .appender("fileout")
                    .build(LevelFilter::Debug),
            )
            .unwrap();
        let handle = log4rs::init_config(config).unwrap();
    }
    fn debug(message: String) -> () {
        debug!("{}", message);
    }
    fn info(message: String) -> () {
        info!("{}", message);
    }
    fn error(message: String) -> () {
        error!("{}", message);
    }
}

#[cfg(not(feature = "local"))]
impl Logger {
    fn init() -> () {}
    fn debug(message: String) -> () {}
    fn info(message: String) -> () {}
    fn error(message: String) -> () {}
}

// main --------------------------------------------------
fn main() -> std::io::Result<()> {
    use std::env;
    env::set_var("RUST_BACKTRACE", "1");
    Logger::init();
    get_time();
    let input = Input::create();
    let solver = Solver::new(input);
    solver.solve();
    Ok(())
}

// input --------------------------------------------------
#[macro_export]
macro_rules! input_value {
    ($f: expr, $x: expr) => {
        let mut line = String::new();
        $f.read_line(&mut line).unwrap();
        $x = line.trim().parse().unwrap();
    };
}

#[macro_export]
macro_rules! input_vec {
    ($f: expr, $x: expr) => {
        let mut line = String::new();
        $f.read_line(&mut line).unwrap();
        let mut iter = line.split_whitespace();
        $x = iter.map(|v| v.parse().unwrap()).collect();
    };
}

struct Input {
    D: i32,
    C: Vec<i32>,
    S: Vec<Vec<i32>>,
}

impl Input {
    fn create() -> Input {
        use std::fs;
        use std::io::{self, BufRead, BufReader};

        // let mut f = File::open(filename).unwrap();
        // let mut contents = String::new();
        // f.read_to_string(&mut contents).expect("something went wrong reading the file");

        // input
        let Z = 26;
        let mut D: i32;
        let mut C: Vec<i32>;
        let mut S: Vec<Vec<i32>>;
        let mut f = BufReader::new(io::stdin());

        let mut f: Box<dyn std::io::BufRead>;
        if cfg!(feature = "local") {
            f = Box::new(BufReader::new(File::open("input/input.txt").unwrap()));
        } else {
            f = Box::new(BufReader::new(std::io::stdin()));
        }

        input_value! {f, D}
        input_vec! {f, C}

        S = Default::default();
        for i in 0..D {
            let x: Vec<i32>;
            input_vec! {f, x}
            S.push(x);
        }

        Input { D, C, S }
    }
}

// solve --------------------------------------------------
struct Solver {
    D: i32,
    C: Vec<i32>,
    S: Vec<Vec<i32>>,
    K: i32,
}

impl Solver {
    pub fn new(input: Input) -> Solver {
        let K = 26;
        Solver {
            D: input.D,
            C: input.C,
            S: input.S,
            K,
        }
    }

    pub fn solve(&self) -> () {
        // parameters
        let seed = 71;
        let t0 = 2000.0;
        let t1 = 600.0;

        let mut rng = rand_pcg::Pcg64Mcg::seed_from_u64(seed);
        let mut annealer = Annealer::new(t0, t1);

        let mut command: Vec<i32> = Default::default();
        for i in 0..self.D {
            command.push(rng.gen_range(0, self.K));
        }
        let mut state = State::new(self, command);
        state.initialize();

        let prob = 0.5;
        let TL = 1.5;
        let mut counter = 0;
        let mut counter_update = 0;
        let mut best_score = state.score;
        let mut best_command: Vec<i32> = state.command.clone();
        let mut elapsed = 0.0;
        Logger::info(format!("first -- {}", best_score));

        while get_time() < TL {
            loop {
                if counter & 0xFF == 0 {
                    elapsed = get_time();
                    if elapsed > TL {
                        Logger::info(format!("turn:{} best_score:{}", counter, best_score));
                        break;
                    }
                    let anneal_t = elapsed / TL;
                    annealer.set_temperature(anneal_t);
                    if counter & 0xFFFF == 0 {
                        Logger::info(format!(
                            "turn:{} temperature:{}",
                            counter, annealer.temperature
                        ));
                    }
                }

                let mut flg = 0;
                let mut p1_r1 = 0;
                let mut p1_r2 = 0;
                let mut p2_d1 = 0;
                let mut p2_v1 = 0;
                let mut new_score: i32;
                if rng.gen::<f64>() < prob {
                    // swap
                    // TODO: enumにしたい
                    flg = 1;
                    loop {
                        let next_range = 16;
                        p1_r1 = rng.gen_range(0, self.D);
                        p1_r2 = p1_r1 + 1 + rng.gen_range(0, next_range);
                        let mut valid = true;
                        if p1_r2 >= self.D || state.command.at(p1_r1) == state.command.at(p1_r2) {
                            valid = false
                        }
                        if valid {
                            break;
                        }
                    }
                    new_score = state.score_swap(p1_r1, p1_r2);
                } else {
                    // update
                    flg = 2;
                    loop {
                        p2_d1 = rng.gen_range(0, self.D);
                        p2_v1 = rng.gen_range(0, self.K);
                        let mut valid = true;
                        if state.command.at(p2_d1) == p2_v1 {
                            valid = false;
                        }
                        if valid {
                            break;
                        }
                    }
                    new_score = state.score_change(p2_d1, p2_v1);
                }

                if annealer.accept(state.score, new_score) {
                    counter_update += 1;

                    if flg == 1 {
                        state.update_swap(p1_r1, p1_r2);
                    }
                    if flg == 2 {
                        state.update_change(p2_d1, p2_v1);
                    }

                    Logger::debug(format!(
                        "accepted score: {} turn:{} flg:{} time:{}",
                        state.score, counter, flg, elapsed
                    ));

                    // ベスト点数を超えている場合はその状態を保存
                    if state.score > best_score {
                        best_score = state.score;
                        best_command = state.command.clone();
                        Logger::info(format!("score:{} at turn:{}", best_score, counter));
                    }
                }
                counter += 1;
            }
        }
        // Logger::debug("Hey Debug!");
        // Logger::info("Hey Info!");

        eprintln!("counter: {} update:{}", counter, counter_update);
        for i in 0..self.D {
            println!("{}", state.command.at(i) + 1);
        }
    }
}

struct Annealer {
    t0: f64,
    t1: f64,
    temperature: f64,
    rng: Mcg128Xsl64,
}

impl Annealer {
    fn new(t0: f64, t1: f64) -> Annealer {
        let seed = 123;
        let rng = rand_pcg::Pcg64Mcg::seed_from_u64(seed);
        Annealer {
            t0,
            t1,
            temperature: t0,
            rng,
        }
    }

    fn set_temperature(&mut self, t: f64) -> () {
        // 指数関数型
        self.temperature = self.t0.powf(1.0 - t) * self.t1.powf(t);
    }

    fn accept(&mut self, prev: i32, curr: i32) -> bool {
        if curr >= prev {
            return true;
        }
        let loss = curr - prev;
        let prob = (loss as f64 / self.temperature).min(1.0).max(0.0);
        return self.rng.gen_bool(prob);
    }
}

struct State<'a> {
    D: i32,
    K: i32,
    C: &'a Vec<i32>,
    S: &'a Vec<Vec<i32>>,

    command: Vec<i32>,
    score: i32,
    // 前のコンテストの日、初期値は-1 -- 常に-1日目に開催されると考えて良い
    last_contest: Vec<Vec<i32>>,
    // 次のコンテストの日、初期値はD -- 常にD日目に開催されると考えて良い
    next_contest: Vec<Vec<i32>>,
}

impl State<'_> {
    pub fn new(solver: &Solver, command: Vec<i32>) -> State {
        let D = solver.D;
        let score = 0;
        let mut last_contest: Vec<Vec<i32>> = Vec::new();
        for i in 0..D {
            let mut v = Vec::new();
            v.resize(solver.K as usize, -1);
            last_contest.push(v);
        }
        let mut next_contest: Vec<Vec<i32>> = Vec::new();
        for i in 0..D {
            let mut v = Vec::new();
            v.resize(solver.K as usize, D);
            next_contest.push(v);
        }
        State {
            D: solver.D,
            K: solver.K,
            C: &solver.C,
            S: &solver.S,
            command,
            score,
            last_contest,
            next_contest,
        }
    }

    pub fn initialize(&mut self) -> () {
        self.score = self.score_calc_raw();

        // 初期化
        let mut last_contest: Vec<Vec<i32>> = Vec::new();
        let mut next_contest: Vec<Vec<i32>> = Vec::new();
        for d in 0..self.D {
            let mut v = Vec::new();
            v.resize(self.K as usize, 0);
            last_contest.push(v);
            let mut v = Vec::new();
            v.resize(self.K as usize, 0);
            next_contest.push(v);
        }

        for k in 0..self.K {
            let mut last = -1;
            for d in 0..self.D {
                *last_contest.mt(d, k) = last;
                if self.command.at(d) == k {
                    last = d;
                }
            }
            let mut next = self.D;
            for d in (0..self.D).rev() {
                *next_contest.mt(d, k) = next;
                if self.command.at(d) == k {
                    next = d;
                }
            }
        }

        self.last_contest = last_contest;
        self.next_contest = next_contest;
    }

    pub fn score_calc_raw(&self) -> i32 {
        let mut score_contests: Vec<i32> = Vec::new();
        score_contests.resize(self.K as usize, 0);

        for k in 0..self.K {
            let mut score_k = 0;
            let mut last = 0;
            for d in 0..self.D {
                if k == self.command.at(d) {
                    score_k += self.S.at(d, k);
                    last = d + 1;
                }
                score_k -= (d + 1 - last) * self.C.at(k);
            }
            *score_contests.mt(k) = score_k;
        }
        score_contests.iter().sum()
    }

    pub fn score_swap(&self, d1: i32, d2: i32) -> i32 {
        // d1にあるコンテスト: k1 -> k2
        // d2にあるコンテスト: k2 -> k1
        // k1 == k2であればそのまま
        let k1 = self.command.at(d1);
        let k2 = self.command.at(d2);
        if k1 == k2 {
            return self.score;
        }

        // 差分計算
        let mut diff = 0;
        diff += self.diff_score_swap(k1, d1, d2);
        // println!("score_swap {} - {} {}=>{}", self.diff_score_swap(k1, d1, d2), k1, d1, d2);
        diff += self.diff_score_swap(k2, d2, d1);
        // println!("score_swap {} - {} {}=>{}", self.diff_score_swap(k2, d2, d1), k2, d2, d1);

        self.score + diff
    }

    pub fn score_change(&self, d: i32, k: i32) -> i32 {
        // dにあるコンテスト: kb -> k
        // kb == kであればそのまま
        let kb = self.command.at(d);
        if kb == k {
            return self.score;
        }

        // 差分計算
        let mut diff = 0;
        diff += self.diff_score_remove(d, kb);
        diff += self.diff_score_add(d, k);

        self.score + diff
    }

    pub fn update_change(&mut self, d: i32, k: i32) -> () {
        let kb = self.command.at(d);
        // debug_assert_eq!(self.score, self.score_calc_raw());
        // println!("evaluate score: {}", self.score);
        // println!("evaluate score_change: {}", self.score_change(d, k));
        self.update_remove(d, kb);
        self.update_add(d, k);

        if self.score != self.score_calc_raw() {
            use itertools;
            Logger::error(format!("d:{} k:{} kb:{} score:{} score_raw:{}",
                                  d, k, kb, self.score, self.score_calc_raw()));
            use std::fs;
            use std::io::{Write, Read, BufWriter, BufReader, copy};
            let mut f = BufWriter::new(fs::File::create("result/output.txt").unwrap());
            for d in 0..self.D{
                f.write(format!("{}\n", self.command.at(d) + 1).as_bytes());
            }
            panic!();
        }
    }

    pub fn update_swap(&mut self, d1: i32, d2: i32) -> () {
        // debug_assert_eq!(self.score, self.score_calc_raw());
        // println!("{}", self.score);
        // println!("evaluate score_swap: {}", self.score_swap(d1, d2));
        let k1 = self.command.at(d1);
        let k2 = self.command.at(d2);
        self.update_swap_single(k1, d1, d2);
        self.update_swap_single(k2, d2, d1);

        if self.score != self.score_calc_raw() {
            use itertools;
            Logger::error(format!("d1:{} d2:{} k1:{} k2:{} score:{} score_raw:{}",
                                  d1, d2, k1, k2, self.score, self.score_calc_raw()));
            use std::fs;
            use std::io::{Write, Read, BufWriter, BufReader, copy};
            let mut f = BufWriter::new(fs::File::create("result/output.txt").unwrap());
            for d in 0..self.D{
                f.write(format!("{}\n", self.command.at(d) + 1).as_bytes());
            }
            panic!();
        }
    }

    fn diff_score_remove(&self, d: i32, k: i32) -> i32 {
        let mut diff = 0;
        diff -= self.S.at(d, k);
        let db1 = (d - 1) - self.last_contest.at(d, k);
        let db2 = self.next_contest.at(d, k) - (d + 1);
        let da1 = self.next_contest.at(d, k) - self.last_contest.at(d, k) - 1;
        let sb = db1 * (db1 + 1) / 2 + db2 * (db2 + 1) / 2;
        let sa = da1 * (da1 + 1) / 2;
        diff -= (-self.C.at(k) * sb);
        diff += (-self.C.at(k) * sa);
        // println!("[{}]remove {} diff:{}", k, d, diff);
        diff
    }

    fn diff_score_add(&self, d: i32, k: i32) -> i32 {
        let mut diff = 0;
        diff += self.S.at(d, k);
        let db1 = self.next_contest.at(d, k) - self.last_contest.at(d, k) - 1;
        let da1 = (d - 1) - self.last_contest.at(d, k);
        let da2 = self.next_contest.at(d, k) - (d + 1);
        let sb = db1 * (db1 + 1) / 2;
        let sa = da1 * (da1 + 1) / 2 + da2 * (da2 + 1) / 2;
        diff -= (-self.C.at(k) * sb);
        diff += (-self.C.at(k) * sa);
        // println!("[{}]add {} diff:{}", k, d, diff);
        diff
    }

    fn diff_score_swap(&self, k: i32, db: i32, da: i32) -> i32 {
        // 重なるように動かすことはないとする
        debug_assert_ne!(da, db);
        debug_assert_ne!(self.next_contest.at(db, k), da);
        debug_assert_ne!(self.last_contest.at(db, k), da);

        // db -> da に動かす場合
        let next = self.next_contest.at(db, k);
        let last = self.last_contest.at(db, k);
        let jump = (da > db && da > next) || (da < db && da < last);

        if jump {
            let mut diff = 0;
            diff += self.diff_score_remove(db, k);
            diff += self.diff_score_add(da, k);
            // println!("[{}]{}->{} next:{} last:{} jump:{} diff:{}", k, db, da, next, last, jump, diff);
            diff
        } else {
            let mut diff = 0;
            diff -= self.S.at(db, k);
            diff += self.S.at(da, k);
            let db1 = (db - 1) - last;
            let db2 = next - (db + 1);
            let da1 = (da - 1) - last;
            let da2 = next - (da + 1);
            let sb = db1 * (db1 + 1) / 2 + db2 * (db2 + 1) / 2;
            let sa = da1 * (da1 + 1) / 2 + da2 * (da2 + 1) / 2;
            diff -= (-self.C.at(k) * sb);
            diff += (-self.C.at(k) * sa);
            // println!("[{}]{}->{} next:{} last:{} jump:{} diff:{}", k, db, da, next, last, jump, diff);
            diff
        }
    }

    fn update_remove(&mut self, dc: i32, k: i32) -> () {
        // commandの削除は省略
        self.score += self.diff_score_remove(dc, k);

        let mut d = dc - 1;
        while d >= 0 && self.next_contest.at(d, k) == dc {
            *self.next_contest.mt(d, k) = self.next_contest.at(dc, k);
            d -= 1;
        }

        let mut d = dc + 1;
        while d < self.D && self.last_contest.at(d, k) == dc {
            *self.last_contest.mt(d, k) = self.last_contest.at(dc, k);
            d += 1;
        }
    }

    fn update_add(&mut self, dc: i32, k: i32) -> () {
        *self.command.mt(dc) = k;
        self.score += self.diff_score_add(dc, k);

        let mut d = dc - 1;
        while d >= 0 && d >= self.last_contest.at(dc, k) {
            *self.next_contest.mt(d, k) = dc;
            d -= 1;
        }

        let mut d = dc + 1;
        while d < self.D && d <= self.next_contest.at(dc, k) {
            *self.last_contest.mt(d, k) = dc;
            d += 1;
        }
    }

    fn update_swap_single(&mut self, k: i32, db: i32, da: i32) -> () {
        // 更新
        let next = self.next_contest.at(db, k);
        let last = self.last_contest.at(db, k);
        let jump = (da > db && da > next) || (da < db && da < last);

        if jump {
            self.update_remove(db, k);
            self.update_add(da, k);
        } else {
            *self.command.mt(da) = k;
            self.score += self.diff_score_swap(k, db, da);
            for d in last..=next {
                if d < 0 || d >= self.D {
                    continue;
                }
                if d != next {
                    if d < da {
                        *self.next_contest.mt(d, k) = da;
                    } else {
                        *self.next_contest.mt(d, k) = next
                    }
                }
                if d != last {
                    if d > da {
                        *self.last_contest.mt(d, k) = da;
                    } else {
                        *self.last_contest.mt(d, k) = last;
                    }
                }
            }
        }
    }
}


Measured memory_bytes: 3564