【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 igoplus y) 个才能成立。然后开始参考出题人题解。先求前 (r-l+1) 个数字的 (sum) 的最高为 (1)的位为 (1),再求 (x) 能否满足要求。

吉老师线段树部分

本部分我参考了HDU的代码

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

push_tag 操作时,对于异或和,根据最小值的数量的奇偶性来判断是否变化。如果是最小值的数量是奇数,则需要异或上 (tg igoplus fi\_min) 才行。((fimin) 是区间最小值),转化过程为:设要异或上的值为 (z),则得到 (fi\_min igoplus z = tg),通过移项可得 (z = tg igoplus 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
");
            } 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
", ans);
            }
        }
    }
}
原文地址:https://www.cnblogs.com/tudouuuuu/p/14177881.html