P9495 「SFCOI-3」进行一个魔的除 I

· · 题解

完全不想思考怎么办?

打个表,通过惊人的注意力可以观察出先后手的操作策略。

a_{-1} = a_{n + 2} = 1a_0 = a_{n + 1} = 0

先手的操作策略是:

后手的操作策略是:

直接模拟可以获得 O(n^2) 的做法。树状数组维护一些东西的前驱后继加速模拟,时间复杂度 O(n \log n),需要卡常。

:::info[代码] 不建议参考。


#include <bits/stdc++.h>
#define int unsigned
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

namespace IO {
    const int maxn = 1 << 20;

    char ibuf[maxn], *iS, *iT, obuf[maxn], *oS = obuf;

    inline char gc() {
        return (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++) : *iS++);
    }

    template<typename T = int>
    inline T read() {
        char c = gc();
        T x = 0;
        bool f = 0;
        while (c < '0' || c > '9') {
            f |= (c == '-');
            c = gc();
        }
        while (c >= '0' && c <= '9') {
            x = (x << 1) + (x << 3) + (c ^ 48);
            c = gc();
        }
        return f ? ~(x - 1) : x;
    }

    inline int reads(char *s) {
        char c = gc();
        int len = 0;
        while (isspace(c)) {
            c = gc();
        }
        while (!isspace(c) && c != -1) {
            s[len++] = c;
            c = gc();
        }
        return len;
    }

    inline void flush() {
        fwrite(obuf, 1, oS - obuf, stdout);
        oS = obuf;
    }

    struct Flusher {
        ~Flusher() {
            flush();
        }
    } AutoFlush;

    inline void pc(char ch) {
        if (oS == obuf + maxn) {
            flush();
        }
        *oS++ = ch;
    }

    template<typename T>
    inline void write(T x) {
        static char stk[64], *tp = stk;
        if (x < 0) {
            x = ~(x - 1);
            pc('-');
        }
        do {
            *tp++ = x % 10;
            x /= 10;
        } while (x);
        while (tp != stk) {
            pc((*--tp) | 48);
        }
    }

    template<typename T>
    inline void writesp(T x) {
        write(x);
        pc(' ');
    }

    template<typename T>
    inline void writeln(T x) {
        write(x);
        pc('\n');
    }
}

using IO::read;
using IO::reads;
using IO::write;
using IO::pc;
using IO::writesp;
using IO::writeln;

const int maxn = 1000100;

int n, a[maxn], pre[maxn], nxt[maxn], pr[maxn], nx[maxn], stk[maxn];
int stk1[maxn * 10], stk2[maxn * 10], stk3[maxn * 10], stk4[maxn * 10], top1, top2, top3, top4;
bool vis[maxn];

struct BIT {
    int n, c[maxn];

    inline void init(int _n) {
        n = _n;
        for (int i = 0; i <= n; ++i) {
            c[i] = 0;
        }
    }

    inline void update(int x, int d) {
        for (int i = x; i <= n; i += (i & (-i))) {
            c[i] += d;
        }
    }

    inline int query(int x) {
        int res = 0;
        for (int i = x; i; i -= (i & (-i))) {
            res += c[i];
        }
        return res;
    }

    inline int kth(int k) {
        if (!k) {
            return 0;
        }
        int s = 0, p = 0;
        (p + 524288 <= n && s + c[p + 524288] < k) ? (s += c[p += 524288]) : 0;
        (p + 262144 <= n && s + c[p + 262144] < k) ? (s += c[p += 262144]) : 0;
        (p + 131072 <= n && s + c[p + 131072] < k) ? (s += c[p += 131072]) : 0;
        (p + 65536 <= n && s + c[p + 65536] < k) ? (s += c[p += 65536]) : 0;
        (p + 32768 <= n && s + c[p + 32768] < k) ? (s += c[p += 32768]) : 0;
        (p + 16384 <= n && s + c[p + 16384] < k) ? (s += c[p += 16384]) : 0;
        (p + 8192 <= n && s + c[p + 8192] < k) ? (s += c[p += 8192]) : 0;
        (p + 4096 <= n && s + c[p + 4096] < k) ? (s += c[p += 4096]) : 0;
        (p + 2048 <= n && s + c[p + 2048] < k) ? (s += c[p += 2048]) : 0;
        (p + 1024 <= n && s + c[p + 1024] < k) ? (s += c[p += 1024]) : 0;
        (p + 512 <= n && s + c[p + 512] < k) ? (s += c[p += 512]) : 0;
        (p + 256 <= n && s + c[p + 256] < k) ? (s += c[p += 256]) : 0;
        (p + 128 <= n && s + c[p + 128] < k) ? (s += c[p += 128]) : 0;
        (p + 64 <= n && s + c[p + 64] < k) ? (s += c[p += 64]) : 0;
        (p + 32 <= n && s + c[p + 32] < k) ? (s += c[p += 32]) : 0;
        (p + 16 <= n && s + c[p + 16] < k) ? (s += c[p += 16]) : 0;
        (p + 8 <= n && s + c[p + 8] < k) ? (s += c[p += 8]) : 0;
        (p + 4 <= n && s + c[p + 4] < k) ? (s += c[p += 4]) : 0;
        (p + 2 <= n && s + c[p + 2] < k) ? (s += c[p += 2]) : 0;
        (p + 1 <= n && s + c[p + 1] < k) ? (s += c[p += 1]) : 0;
        return p + 1;
    }

    inline int prev(int x) {
        int t = query(x);
        return t == 1 ? 0 : kth(t - 1);
    }

    inline int next(int x) {
        int t = query(x);
        return kth(t + 1);
    }
} T1, T2, T3, T4, T5;

void solve() {
    n = read();
    for (int i = 0; i <= n + 1; ++i) {
        pre[i] = nxt[i] = pr[i] = nx[i] = 0;
        vis[i] = 0;
    }
    for (int i = 1; i <= n; ++i) {
        a[i] = read();
    }
    a[0] = a[n + 1] = 0;
    top1 = top2 = top3 = top4 = 0;
    T1.init(n);
    T2.init(n);
    T3.init(n + 1);
    T4.init(n);
    T5.init(n);
    int lst = 0, cnt = 0, lst2 = 0, lst3 = 0;
    nx[0] = n + 1;
    for (int i = 1; i <= n; ++i) {
        if (a[i]) {
            ++cnt;
            T1.update(i, 1);
            pre[i] = lst;
            nxt[i] = n + 1;
            if (lst) {
                nxt[lst] = i;
                if (i - lst == 1) {
                    stk1[++top1] = lst;
                }
                if ((i - lst) % 2 == 0) {
                    stk2[++top2] = lst;
                }
            }
            lst = i;
        } else {
            T2.update(i, 1);
            if (i & 1) {
                T4.update(i, 1);
            } else {
                T5.update(i, 1);
            }
            stk4[++top4] = i;
            pre[i] = lst2;
            nxt[i] = n + 1;
            if (lst2) {
                nxt[lst2] = i;
            }
            if ((i - lst2) % 2 == 0) {
                T3.update(i, 1);
                pr[i] = lst3;
                nx[i] = n + 1;
                nx[lst3] = i;
                stk3[++top3] = lst3;
                vis[lst3] = 1;
                lst3 = i;
            }
            lst2 = i;
        }
    }
    T3.update(n + 1, 1);
    pr[n + 1] = lst3;
    nx[n + 1] = n + 2;
    nx[lst3] = n + 1;
    stk3[++top3] = lst3;
    vis[lst3] = 1;
    auto add = [&](int i) -> void {
        if (i == n + 1) {
            int j = pr[i];
            if (!vis[j]) {
                stk3[++top3] = j;
                vis[j] = 1;
            }
            return;
        }
        T3.update(i, 1);
        int t = T3.query(i);
        int j = T3.kth(t - 1), k = T3.kth(t + 1);
        pr[i] = j;
        nx[i] = k;
        nx[j] = i;
        if (!vis[j]) {
            stk3[++top3] = j;
            vis[j] = 1;
        }
        if (!vis[i]) {
            stk3[++top3] = i;
            vis[i] = 1;
        }
        pr[k] = i;
    };
    auto del = [&](int i) -> void {
        if (i == n + 1) {
            int j = pr[i];
            if (!vis[j]) {
                stk3[++top3] = j;
                vis[j] = 1;
            }
            return;
        }
        T3.update(i, -1);
        int j = pr[i], k = nx[i];
        nx[j] = k;
        if (!vis[j]) {
            stk3[++top3] = j;
            vis[j] = 1;
        }
        pr[k] = j;
        pr[i] = nx[i] = 0;
    };
    auto set0 = [&](int i) -> void {
        if (!a[i]) {
            return;
        }
        --cnt;
        a[i] = 0;
        stk4[++top4] = i;
        int j = pre[i], k = nxt[i];
        T1.update(i, -1);
        if (i & 1) {
            T4.update(i, 1);
        } else {
            T5.update(i, 1);
        }
        if (j) {
            nxt[j] = k;
        }
        if (k <= n) {
            pre[k] = j;
        }
        if (j && k <= n) {
            if (k - j == 1) {
                stk1[++top1] = j;
            }
            if ((k - j) % 2 == 0) {
                stk2[++top2] = j;
            }
        }
        T2.update(i, 1);
        int t = T2.query(i);
        j = k = -1;
        for (int p = i - 1; p >= 0 && p >= i - 3; --p) {
            if (!a[p]) {
                j = p;
                break;
            }
        }
        if ((signed)j == -1) {
            j = T2.kth(t - 1);
        }
        for (int p = i + 1; p <= n + 1 && p <= i + 3; ++p) {
            if (!a[p]) {
                k = p;
                break;
            }
        }
        if ((signed)k == -1) {
            k = T2.kth(t + 1);
        }
        pre[i] = j;
        nxt[i] = k;
        nxt[j] = i;
        pre[k] = i;
        int d = 0;
        if ((k - j) % 2 == 0) {
            --d;
        }
        if ((k - i) % 2 == 0) {
            ++d;
        }
        if (d == 1) {
            add(k);
        } else if ((signed)d == -1) {
            del(k);
        }
        if ((i - j) % 2 == 0) {
            add(i);
        }
    };
    auto set1 = [&](int i) -> void {
        if (a[i]) {
            return;
        }
        ++cnt;
        a[i] = 1;
        T1.update(i, 1);
        T2.update(i, -1);
        if (i & 1) {
            T4.update(i, -1);
        } else {
            T5.update(i, -1);
        }
        int j = pre[i], k = nxt[i];
        nxt[j] = k;
        if ((i - j) % 2 == 0) {
            del(i);
        }
        pre[k] = j;
        int d = 0;
        if ((k - i) % 2 == 0) {
            --d;
        }
        if ((k - j) % 2 == 0) {
            ++d;
        }
        if (d == 1) {
            add(k);
        } else if ((signed)d == -1) {
            del(k);
        }
        int t = T1.query(i);
        j = k = -1;
        for (int p = i - 1; p >= 0 && p >= i - 3; --p) {
            if (a[p]) {
                j = p;
                break;
            }
        }
        if ((signed)j == -1) {
            j = T1.kth(t - 1);
        }
        for (int p = i + 1; p <= n + 1 && p <= i + 3; ++p) {
            if (a[p]) {
                k = p;
                break;
            }
        }
        if ((signed)k == -1) {
            k = T1.kth(t + 1);
        }
        pre[i] = j;
        nxt[i] = k;
        if (j) {
            nxt[j] = i;
            if (i - j == 1) {
                stk1[++top1] = j;
            }
            if ((i - j) % 2 == 0) {
                stk2[++top2] = j;
            }
        }
        if (k <= n) {
            pre[k] = i;
            if (k - i == 1) {
                stk1[++top1] = i;
            }
            if ((k - i) % 2 == 0) {
                stk2[++top2] = i;
            }
        }
    };
    int ans = 0, o = 1;
    while (1) {
        if (cnt == n) {
            writeln(ans);
            return;
        }
        if (o) {
            if (!cnt) {
                o ^= 1;
                continue;
            }
            while (top1) {
                int i = stk1[top1];
                if (a[i] && a[i + 1]) {
                    break;
                } else {
                    --top1;
                }
            }
            if (top1) {
                int p = stk1[top1];
                set0(p);
                set0(p + 1);
            } else {
                int x = T1.kth(1);
                if (x & 1) {
                    set0(x);
                } else {
                    x = T1.kth(cnt);
                    if ((n - x) % 2 == 0) {
                        set0(x);
                    } else {
                        while (top2) {
                            int i = stk2[top2];
                            if (a[i] && (nxt[i] - i) % 2 == 0) {
                                break;
                            } else {
                                --top2;
                            }
                        }
                        if (top2) {
                            x = stk2[top2];
                        }
                        set0(x);
                    }
                }
            }
        } else {
            ++ans;
            int k = 0;
            while (top3 && k < 3) {
                int i = stk3[top3--];
                vis[i] = 0;
                if (i == n + 1) {
                    continue;
                }
                if (!a[i] && nx[i]) {
                    int j = pre[nx[i]];
                    if (nx[i] >= n + 1) {
                        j = n + 1;
                    }
                    if (i >= j) {
                        continue;
                    }
                    if ((i + 1) & 1) {
                        int p = T4.query(i) + 1, q = T4.query(j - 1);
                        while (k < 3 && p <= q) {
                            int t = T4.kth(p);
                            if ((nxt[t] - t) % 2 == 0) {
                                break;
                            }
                            stk[++k] = t;
                            ++p;
                        }
                    } else {
                        int p = T5.query(i) + 1, q = T5.query(j - 1);
                        while (k < 3 && p <= q) {
                            int t = T5.kth(p);
                            if ((nxt[t] - t) % 2 == 0) {
                                break;
                            }
                            stk[++k] = t;
                            ++p;
                        }
                    }
                }
            }
            for (int i = 1; i <= k; ++i) {
                set1(stk[i]);
            }
            while (top4 && k < 3) {
                int i = stk4[top4--];
                if (!a[i]) {
                    set1(i);
                    ++k;
                }
            }
        }
        o ^= 1;
    }
}

signed main() {
    int T = read();
    while (T--) {
        solve();
    }
    return 0;
}
:::

这个做法注意力要求极高,代码实现和调试极其困难,在考场上不可能用这个做法通过,所以大家当看个乐子就行。