zoukankan      html  css  js  c++  java
  • 【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);
                }
            }
        }
    }
    
  • 相关阅读:
    文件上传漏洞全面渗透姿势总结
    注册frpc为windows服务,可在未登录用户时启动
    SpringIOC 容器注入方式
    如何交换 Integer 的值?
    分布式websocket服务器
    win10安装Hbase2.3.0
    hadoop常用的命令
    win10安装kafka 2.122.8.1
    win10激活码
    win10 flume source为spooldir,输出到hdfs
  • 原文地址:https://www.cnblogs.com/tudouuuuu/p/14177881.html
Copyright © 2011-2022 走看看