バイトの競プロメモ

主に競技プログラミング

C - 茶碗と豆 AtCoder Regular Contest 038

C: 茶碗と豆 - AtCoder Regular Contest 038 | AtCoder

豆一つをnimの山として考えられる。
よって、全ての茶碗についてgrundy数を求め、A[i]回xorを取った物たちをxorすればいい。

難しいのはgrundy数を求める方法。
区間 [ i - c[i] , i ) に含まれない最小の非負整数を10^5回求める必要がある。

上を言い換えると[i]のgrundy数は、以下の条件を満たす最大のKである。
[ i - c[i] , i )にgrundy数が0 ~ k-1となる物が含まれる。
↑条件1

これは、RMQのセグメント木で
[i] := grundy数がiとなる最大のインデックス(iを0から順に見ており、区間は右にズレていくため、最大のほうが得だから)
[l r):= [ [l] [r]) で最小のインデックスとすれば

grundy数が[0 k)の物の内、最小のインデックスが分かる。
この数は必ずi未満のため、i-c[i]以上なら、[i-c[i] , i) で条件1を満たす。

条件1を満たす数はoooooooxxxxxのようになるため、二分探索で解ける。

よって、O(N log n log n)で解けた
Submission #3889989 - AtCoder Regular Contest 038 | AtCoder

struct Monoid {
    ll i, v;
    Monoid(ll i, ll v) : i(i), v(v) {}
};
#define segMinl [](Monoid a,Monoid b){return a.v <= b.v ? a : b;} , Monoid(-1,MAX(ll))
#define segMinr [](Monoid a,Monoid b){return a.v < b.v ? a : b;} , Monoid(-1,MAX(ll))
#define segMaxl [](Monoid a,Monoid b){return a.v >= b.v ? a : b;} , Monoid(-1,MIN(ll))
#define segMaxr [](Monoid a,Monoid b){return a.v > b.v ? a : b;} , Monoid(-1,MIN(ll))
#define segSum  [](Monoid a,Monoid b){return Monoid(a.i+b.i,a.v+b.v);} , Monoid(0,0)

struct SegmentTree {
    using func=function<Monoid(Monoid, Monoid)>;
    int n;
    vector<Monoid> seg;
    const func f;
    const Monoid e;
    SegmentTree(int len, func f, const Monoid e) : f(f), e(e) {
        n = 1;
        while (n < len)n *= 2;
        seg.assign(2 * n - 1, e);
    }
    SegmentTree(vi dat, const func f, const Monoid e) : f(f), e(e) {
        n = 1;
        int asz = dat.size();
        while (n < asz)n *= 2;
        seg.assign(2 * n - 1, e);
        rep(i, asz) seg[i + n - 1] = Monoid(i, dat[i]);
        rer(i, n - 2)seg[i] = f(seg[i * 2 + 1], seg[i * 2 + 2]);
    }
    void update(int k, int v) {
        seg[k + n - 1] = Monoid(k, v);
        k += n - 1;
        while (k) {
            k = (k - 1) / 2;
            seg[k] = f(seg[k * 2 + 1], seg[k * 2 + 2]);
        }
    }
    void add(int k, int v) {
        update(k, v + seg[k + n - 1].v);
    }
    Monoid query(int a, int b, int k = 0, int l = 0, int r = -1) {
        if (r == -1)r = n;
        if (r <= a || b <= l)return e;
        else if (a <= l && r <= b)return seg[k];
        else {
            Monoid sl = query(a, b, k * 2 + 1, l, (l + r) / 2);
            Monoid sr = query(a, b, k * 2 + 2, (l + r) / 2, r);
            return f(sl, sr);
        }
    }
    int geti(int a, int b) {
        return query(a, b).i;
    }
    ll getv(int a, int b) {
        return query(a, b).v;
    }
    Monoid operator[](int k) const {
        return seg[k + n];
    }
};
int N, K, M, H, W, X, Y, q, Q, r;
vi A, B, C;
signed main() {
    cin >> N;
    add2(C, A, N - 1);
    //[i] grundy数が0~k-1となる者たちのインデックス の最小値
    //[l r) のインデックスのうちの最小
    SegmentTree st(vi(k5, -1), segMinr);
    st.update(0, 0);
    int gr;
    int grgr = 0;
    //iのgrundy数を求めたい
    rep(i, 1, N) {
        //seg[0 K) >= i-c[i] となる最大のK
        int ok = 0, ng = k6;
        while (abs(ok - ng) > 1) {
            int mid = (ok + ng) / 2;
            if (st.getv(0, mid) >= i - C[i - 1])ok = mid;
            else ng = mid;
        }
        gr = ok;
        st.update(gr, i);
        if (A[i - 1] % 2)grgr ^= gr;
    }
    if (grgr)cout << "First" << endl;
    else cout << "Second" << endl;

    return 0;
}