제출 #1330782

#제출 시각아이디문제언어결과실행 시간메모리
1330782TheMiraiTraveller0711Languages (IOI10_languages)C++20
95 / 100
706 ms144072 KiB
#include "grader.h"
#include <bits/stdc++.h>
using namespace std;

static constexpr int LANGS = 56;

// More bins reduces collisions (big deal above ~0.85).
// 2^18 => per table: 262144 * 56 * 2 bytes ≈ 28.1 MiB
static constexpr int BINS_POW = 18;
static constexpr int BINS = 1 << BINS_POW;
static constexpr uint32_t MASK = BINS - 1;

// Gram weights (tune here). You said weight on 1,2,4; keep those strong,
// but enabling 3 & 5 usually boosts from ~0.86 -> ~0.91+.
static constexpr int W1 = 1;
static constexpr int W2 = 3;
static constexpr int W3 = 5;
static constexpr int W4 = 7;
static constexpr int W5 = 6;

// Unigram frequency clip: counts above this are capped.
static constexpr int UNI_CLIP = 3;

// Small bias toward seen languages (helps early); keep small.
static constexpr int BIAS_W = 1;

// If prediction is correct but margin is small, still do a tiny update vs runner-up.
static constexpr int MARGIN_SHARPEN = 30;   // try 20..80
static constexpr int SHARPEN_STEP = 1;      // keep small

static vector<int16_t> tab1, tab2, tab3, tab4, tab5;
static int seen_cnt[LANGS];
static int total_seen = 0;

static inline uint64_t splitmix64(uint64_t x) {
    x += 0x9e3779b97f4a7c15ULL;
    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
    x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
    return x ^ (x >> 31);
}

// Signed feature hashing: store key = (bin<<1) | negBit
static inline uint32_t hash_to_key(uint64_t raw) {
    uint64_t h = splitmix64(raw);
    uint32_t bin = (uint32_t)(h & MASK);
    uint32_t neg = (uint32_t)((h >> 63) & 1ULL); // top bit as sign
    return (bin << 1) | neg;
}
static inline int key_sign(uint32_t key) {
    return (key & 1U) ? -1 : +1;
}
static inline uint32_t key_bin(uint32_t key) {
    return key >> 1;
}

static inline void sat_inc(vector<int16_t>& tab, uint32_t bin, int lang, int delta) {
    int idx = (int)bin * LANGS + lang;
    int v = (int)tab[idx] + delta;
    if (v > 32767) v = 32767;
    if (v < -32768) v = -32768;
    tab[idx] = (int16_t)v;
}

// ---------- feature builders ----------

// 1-gram: keep clipped counts (frequency)
static inline void make_features_1(const int E[100], vector<pair<uint32_t,uint8_t>>& out) {
    static vector<uint32_t> tmp;
    tmp.clear();
    tmp.reserve(100);
    for (int i = 0; i < 100; i++) {
        // mix symbol with a salt so different n-grams don't collide identically
        uint64_t raw = (uint64_t)(uint32_t)E[i] ^ 0x123456789ABCDEF0ULL;
        tmp.push_back(hash_to_key(raw));
    }
    sort(tmp.begin(), tmp.end());

    out.clear();
    out.reserve(64);

    for (int i = 0; i < (int)tmp.size();) {
        int j = i + 1;
        while (j < (int)tmp.size() && tmp[j] == tmp[i]) j++;
        int c = j - i;
        if (c > UNI_CLIP) c = UNI_CLIP;
        out.push_back({tmp[i], (uint8_t)c});
        i = j;
    }
}

// 2..5-gram: presence (unique)
template<int N>
static inline void make_features_ngram(const int E[100], vector<uint32_t>& out, uint64_t salt) {
    static_assert(N >= 2 && N <= 5, "N must be 2..5");
    out.clear();
    out.reserve(100);

    for (int i = 0; i + (N - 1) < 100; i++) {
        // pack up to 5 symbols (each <= 65535) into 64-bit by mixing, not direct packing (packing 5*16=80 bits).
        uint64_t x = salt;
        for (int k = 0; k < N; k++) {
            x ^= (uint64_t)(uint32_t)E[i + k] + 0x9e3779b97f4a7c15ULL + (x << 6) + (x >> 2);
        }
        out.push_back(hash_to_key(x));
    }

    sort(out.begin(), out.end());
    out.erase(unique(out.begin(), out.end()), out.end());
}

// ---------- predict & update ----------

struct PredResult {
    int best, second;
    int32_t bestScore, secondScore;
};

static inline PredResult predict_lang(const vector<pair<uint32_t,uint8_t>>& f1,
                                      const vector<uint32_t>& f2,
                                      const vector<uint32_t>& f3,
                                      const vector<uint32_t>& f4,
                                      const vector<uint32_t>& f5) {
    int32_t score[LANGS];
    for (int l = 0; l < LANGS; l++) score[l] = (int32_t)seen_cnt[l] * BIAS_W;

    // 1-gram with counts
    for (auto [key, cnt] : f1) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        int base = (int)bin * LANGS;
        int mult = sgn * (int)cnt * W1;
        for (int l = 0; l < LANGS; l++) score[l] += (int32_t)tab1[base + l] * mult;
    }
    // 2-gram
    for (uint32_t key : f2) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        int base = (int)bin * LANGS;
        int mult = sgn * W2;
        for (int l = 0; l < LANGS; l++) score[l] += (int32_t)tab2[base + l] * mult;
    }
    // 3-gram
    for (uint32_t key : f3) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        int base = (int)bin * LANGS;
        int mult = sgn * W3;
        for (int l = 0; l < LANGS; l++) score[l] += (int32_t)tab3[base + l] * mult;
    }
    // 4-gram
    for (uint32_t key : f4) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        int base = (int)bin * LANGS;
        int mult = sgn * W4;
        for (int l = 0; l < LANGS; l++) score[l] += (int32_t)tab4[base + l] * mult;
    }
    // 5-gram
    for (uint32_t key : f5) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        int base = (int)bin * LANGS;
        int mult = sgn * W5;
        for (int l = 0; l < LANGS; l++) score[l] += (int32_t)tab5[base + l] * mult;
    }

    int best = 0, second = 1;
    if (score[second] > score[best]) swap(best, second);
    for (int l = 2; l < LANGS; l++) {
        if (score[l] > score[best]) { second = best; best = l; }
        else if (score[l] > score[second]) { second = l; }
    }

    // tie-break toward less seen (gentle exploration)
    if (score[second] == score[best] && seen_cnt[second] < seen_cnt[best]) swap(best, second);

    return {best, second, score[best], score[second]};
}

static inline void update_model(int trueL, int predL,
                                const vector<pair<uint32_t,uint8_t>>& f1,
                                const vector<uint32_t>& f2,
                                const vector<uint32_t>& f3,
                                const vector<uint32_t>& f4,
                                const vector<uint32_t>& f5,
                                int step_true = 1,
                                int step_pred = 1) {
    // 1-gram
    for (auto [key, cnt] : f1) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        int d = sgn * (int)cnt;
        sat_inc(tab1, bin, trueL, +d * step_true);
        if (predL != trueL) sat_inc(tab1, bin, predL, -d * step_pred);
    }
    // 2-gram
    for (uint32_t key : f2) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        sat_inc(tab2, bin, trueL, +sgn * step_true);
        if (predL != trueL) sat_inc(tab2, bin, predL, -sgn * step_pred);
    }
    // 3-gram
    for (uint32_t key : f3) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        sat_inc(tab3, bin, trueL, +sgn * step_true);
        if (predL != trueL) sat_inc(tab3, bin, predL, -sgn * step_pred);
    }
    // 4-gram
    for (uint32_t key : f4) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        sat_inc(tab4, bin, trueL, +sgn * step_true);
        if (predL != trueL) sat_inc(tab4, bin, predL, -sgn * step_pred);
    }
    // 5-gram
    for (uint32_t key : f5) {
        uint32_t bin = key_bin(key);
        int sgn = key_sign(key);
        sat_inc(tab5, bin, trueL, +sgn * step_true);
        if (predL != trueL) sat_inc(tab5, bin, predL, -sgn * step_pred);
    }

    seen_cnt[trueL]++;
    total_seen++;
}

void excerpt(int E[100]) {
    static bool inited = false;
    if (!inited) {
        tab1.assign((size_t)BINS * LANGS, 0);
        tab2.assign((size_t)BINS * LANGS, 0);
        tab3.assign((size_t)BINS * LANGS, 0);
        tab4.assign((size_t)BINS * LANGS, 0);
        tab5.assign((size_t)BINS * LANGS, 0);
        memset(seen_cnt, 0, sizeof(seen_cnt));
        inited = true;
    }

    vector<pair<uint32_t,uint8_t>> f1;
    vector<uint32_t> f2, f3, f4, f5;

    make_features_1(E, f1);
    make_features_ngram<2>(E, f2, 0xA5A5A5A5A5A5A5A5ULL);
    make_features_ngram<3>(E, f3, 0xC3C3C3C3C3C3C3C3ULL);
    make_features_ngram<4>(E, f4, 0x5F5F5F5F5F5F5F5FULL);
    make_features_ngram<5>(E, f5, 0x9B9B9B9B9B9B9B9BULL);

    PredResult pr = predict_lang(f1, f2, f3, f4, f5);
    int guess = pr.best;

    int correct = language(guess);

    if (correct != guess) {
        // Standard perceptron correction
        update_model(correct, guess, f1, f2, f3, f4, f5, /*step_true=*/1, /*step_pred=*/1);
    } else {
        // If it's correct but margin is tiny, sharpen vs runner-up (reduces future flips)
        int32_t margin = pr.bestScore - pr.secondScore;
        if (margin < MARGIN_SHARPEN) {
            update_model(correct, pr.second, f1, f2, f3, f4, f5,
                         /*step_true=*/SHARPEN_STEP, /*step_pred=*/SHARPEN_STEP);
        } else {
            // still count it as seen
            seen_cnt[correct]++;
            total_seen++;
        }
    }
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...