【2020ICPC南京J】Just Another Game of Stones(Nim博弈+吉老师线段树)

题目链接:https://ac.nowcoder.com/acm/contest/10272/J
出题人题解链接:https://zhuanlan.zhihu.com/p/338249705

题目大意

给定一串石子堆\(b_{i}\),有如下两种操作:

  1. 将区间 \([l,r]\) 中的石子堆中的石子数量变为 \(max(b_{i}, x)\)

  2. 取出区间 \([l,r]\) 中的石子堆,新增第 \(r-l+2\) 个石子堆,其中石子数量为 \(x\)。询问有多少种方法拿石子使得转变为必胜态。

思路

看到区间变化为 \(max(b_{i}, x)\) -> 吉老师线段树!

Nim博弈中,必胜态为异或和为0,将题意转成有多少种方法拿石子使得最后的异或和为0。

  1. 若当前 \(r-l+1\) 个石子堆的异或和为 \(0\),则不能够移动石子,输出 \(0\)

  2. 若当前 \(r-l+1\) 个石子堆的异或和不为 \(0\)。设当前异或和为 \(sum\),需要操作的石子堆个数为 \(y\),则需要拿走 \(y-sum \bigoplus y\) 个才能成立。然后开始参考出题人题解。先求前 \(r-l+1\) 个数字的 \(sum\) 的最高为 \(1\)的位为 \(1\),再求 \(x\) 能否满足要求。

吉老师线段树部分

本部分我参考了HDU的代码

push_up 操作时,维护一个最小值次小值、二进制当前区间在这个位是1的个数之和。

push_tag 操作时,对于异或和,根据最小值的数量的奇偶性来判断是否变化。如果是最小值的数量是奇数,则需要异或上 \(tg \bigoplus fi\_min\) 才行。(\(fi\min\) 是区间最小值),转化过程为:设要异或上的值为 \(z\),则得到 \(fi\_min \bigoplus z = tg\),通过移项可得 \(z = tg \bigoplus fi\_min\);如果最小值的数量为偶数,则什么都不做。然后修改二进制当前区间在这个位是1的个数之和的值。再是吉老师线段树 push_tag 的常规操作。

注意:因为一开始使用的 \(inf\) 设定为 \(0x3f3f3f3f\),一直在RE,事实上这个数字是比 \(2^{30}-1\) 要小的,导致在修改时会一直往下走。

AC代码

#include <bits/stdc++.h>

#define inf (2e9)
using namespace std;
const int MAXN = 4e5 + 10;
int a[MAXN];

class JLS {
public:
    struct node {
        int l, r;
        int fi_min, se_min;
        int cnt_min;
        int sum, bit[31];
    } T[MAXN << 2];
    int lazy[MAXN << 2];
#define lson rt<<1
#define rson rt<<1|1

    inline void push_up(int rt) {
        T[rt].sum = T[lson].sum ^ T[rson].sum;
        for (int i = 0; i < 31; i++) T[rt].bit[i] = T[lson].bit[i] + T[rson].bit[i];
        if (T[lson].fi_min == T[rson].fi_min) {
            T[rt].fi_min = T[lson].fi_min;
            T[rt].se_min = min(T[lson].se_min, T[rson].se_min);
            T[rt].cnt_min = T[lson].cnt_min + T[rson].cnt_min;
        } else if (T[lson].fi_min < T[rson].fi_min) {
            T[rt].fi_min = T[lson].fi_min;
            T[rt].se_min = min(T[lson].se_min, T[rson].fi_min);
            T[rt].cnt_min = T[lson].cnt_min;
        } else {
            T[rt].fi_min = T[rson].fi_min;
            T[rt].se_min = min(T[lson].fi_min, T[rson].se_min);
            T[rt].cnt_min = T[rson].cnt_min;
        }
    }

    inline void push_tag(int rt, int tg) {
        if (T[rt].fi_min >= tg) return;
        T[rt].sum ^= (T[rt].cnt_min & 1) * (T[rt].fi_min ^ tg);
        for (int i = 0; i < 31; i++) {
            if (T[rt].fi_min >> i & 1)T[rt].bit[i] -= T[rt].cnt_min;
            if (tg >> i & 1) T[rt].bit[i] += T[rt].cnt_min;
        }
        T[rt].fi_min = lazy[rt] = tg;
    }

    inline void push_down(int rt) {
        if (lazy[rt] != -1) {
            push_tag(lson, lazy[rt]), push_tag(rson, lazy[rt]);
            lazy[rt] = -1;
        }
    }

    void build(int rt, int l, int r) {
        T[rt].l = l, T[rt].r = r;
        lazy[rt] = -1;
        if (l == r) {
            T[rt].sum = T[rt].fi_min = a[l];
            T[rt].se_min = inf;
            T[rt].cnt_min = 1;
            for (int i = 0; i < 31; i++) {
                T[rt].bit[i] = (a[l] >> i) & 1;
            }
            return;
        }
        int mid = (l + r) >> 1;
        build(lson, l, mid), build(rson, mid + 1, r);
        push_up(rt);
    }

    void update(int rt, int L, int R, int v) {
        // assert(rt < (MAXN<<2));
        if (v <= T[rt].fi_min) return;
        if (L <= T[rt].l && T[rt].r <= R && T[rt].se_min > v) {
            push_tag(rt, v);
            return;
        }
        push_down(rt);
        int mid = (T[rt].l + T[rt].r) >> 1;
        if (L <= mid) update(lson, L, R, v);
        if (R > mid) update(rson, L, R, v);
        push_up(rt);
    }

    int query_sum(int rt, int L, int R) {
        if (L <= T[rt].l && T[rt].r <= R) return T[rt].sum;
        push_down(rt);
        int mid = (T[rt].l + T[rt].r) >> 1;
        int ans = 0;
        if (L <= mid) ans ^= query_sum(lson, L, R);
        if (R > mid) ans ^= query_sum(rson, L, R);
        return ans;
    }

    int query_bit(int rt, int L, int R, int pos) {
        if (L <= T[rt].l && T[rt].r <= R) return T[rt].bit[pos];
        push_down(rt);
        int mid = (T[rt].l + T[rt].r) >> 1;
        int ans = 0;
        if (L <= mid) ans += query_bit(lson, L, R, pos);
        if (R > mid) ans += query_bit(rson, L, R, pos);
        return ans;
    }
    
} tree;

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    tree.build(1, 1, n);
    while (q--) {
        int opt, l, r, x;
        scanf("%d%d%d%d", &opt, &l, &r, &x);
        if (opt == 1) {
            tree.update(1, l, r, x);
        } else {
            int res = tree.query_sum(1, l, r);
            res ^= x;
            if (res == 0) {
                printf("0\n");
            } else {
                int ans = 0, k = 0;
                for (int i = 30; i >= 0; i--) {
                    if ((res >> i) & 1) {
                        k = i;
                        break;
                    }
                }
                ans = tree.query_bit(1, l, r, k) + ((x >> k) & 1);
                printf("%d\n", ans);
            }
        }
    }
}
posted @ 2020-12-23 12:06  tudouuuuu  阅读(229)  评论(0编辑  收藏  举报