ICPC 2020 南京站J Just Another Game of Stones

题意

给出\(n\)堆石子,数量分别是\(a_1 - a_n\),有两种操作:

  • 修改操作,给出\(l, r, x\),对\(a[l \cdots r]\)\(x\)进行区间取\(\text{max}操作\),即\(a_i = max(a_i, x)\)

  • 查询操作,给出\(l, r, x\),将\(a[l \cdots r]\)与数量为\(x\)的一堆石子一共\(r - l + 2\)堆石子拿出来玩nim游戏,问先手获胜的方法有几种

做法

大型缝合怪题。

场上yy了一个神奇的线段树套01Tire的做法,我自己都觉得写不出来。

先用nim游戏的结论,求出\(a[l \cdots r]\)\(x\)的异或和\(s\),如果\(s = 0\)是先手必败,否则就是询问区间中有多少个数(包括\(x\))满足\(a_i \bigotimes s \leqslant s\)

这个东西稍微推一下就可以发现,是询问区间中\(s\)的最高位为\(1\)的数有多少个,证明很简单。

然后看到区间取max的操作,应该是一个segment beats,发现只要维护区间每一位为1的数的数量,套上板子,可以维护。

其实,这个题到这里应该就做完了,因为通过枚举位数可以知道区间的异或和,然后我就偷懒少维护了一个异或和,然后T成sb。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

const int N = 2e5 + 5;
const int M = 30;
const ll P = 998244353LL;
const int inf = 1 << 30;

int n, qn, a[N];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;   

namespace Fwrite {
    const int L = 1 << 15;
    
    char buf[L], *pp = buf;
    
    void Putchar(const char c) {
        if(pp - buf == L) fwrite(buf, 1, L, stdout), pp = buf;
        *pp++ = c;
    }
    
    template<typename T>
    void print(T x) {
        if(x < 0) {
            Putchar('-');
            x = -x;
        }
        if(x > 9) print(x / 10);
        Putchar(x % 10 + '0');
    }
    
    void fsh() {
        fwrite(buf, 1, pp - buf, stdout);
        pp = buf;
    }
    
    template <typename T>
    inline void write(T x, char ch = 0) {
        print(x);
        if (ch != 0) Putchar(ch);
        fsh();
    }

} using Fwrite::write;

namespace SegT {
    int mn[N << 2], sec[N << 2], mnCnt[N << 2], cnt[M][N << 2], tag[N << 2], sum[N << 2];

    #define lc (p << 1)
    #define rc (p << 1 | 1)
    #define mid ((l + r) >> 1)

    inline void up(int p) {
        sum[p] = sum[lc] ^ sum[rc];
        for (int i = 0; i < 30; i++) cnt[i][p] = cnt[i][lc] + cnt[i][rc];
        if (mn[lc] < mn[rc]) {
            mn[p] = mn[lc];
            mnCnt[p] = mnCnt[lc];
            sec[p] = min(sec[lc], mn[rc]);
        } else if (mn[lc] == mn[rc]) {
            mn[p] = mn[lc];
            mnCnt[p] = mnCnt[lc] + mnCnt[rc];
            sec[p] = min(sec[lc], sec[rc]);
        } else {
            mn[p] = mn[rc];
            mnCnt[p] = mnCnt[rc];
            sec[p] = min(sec[rc], mn[lc]);
        }
    }

    inline void down(int p) {
        if (tag[p] > mn[lc] && tag[p] < sec[lc]) {
            for (int i = 0; i < 30; i++) {
                int now = 1 << i;
                if (now > mn[lc] && now > tag[p]) break;
                if (mn[lc] & now) cnt[i][lc] -= mnCnt[lc];
                if (tag[p] & now) cnt[i][lc] += mnCnt[lc];
            }
            if (mnCnt[lc] & 1) sum[lc] ^= mn[lc] ^ tag[p];
            mn[lc] = tag[p];
            tag[lc] = tag[p];
        }
        if (tag[p] > mn[rc] && tag[p] < sec[rc]) {
            for (int i = 0; i < 30; i++) {
                int now = 1 << i;
                if (now > mn[rc] && now > tag[p]) break;
                if (mn[rc] & now) cnt[i][rc] -= mnCnt[rc];
                if (tag[p] & now) cnt[i][rc] += mnCnt[rc];
            }
            if (mnCnt[rc] & 1) sum[rc] ^= mn[rc] ^ tag[p];
            mn[rc] = tag[p];
            tag[rc] = tag[p];
        }
    }

    void build(int p, int l, int r) {
        if (l == r) {
            tag[p] = mn[p] = sum[p] = a[l];
            for (int i = 0; i < 30; i++)
                if (a[l] & (1 << i)) ++cnt[i][p];
            sec[p] = inf;
            mnCnt[p] = 1;
            return;
        }
        build(lc, l, mid), build(rc, mid + 1, r);
        up(p);
    }

    void getMax(int p, int l, int r, int x, int y, int v) {
        if (x <= l && y >= r) {
            if (v <= mn[p]) return;
            else if (v > mn[p] && v < sec[p]) {
                for (int i = 0; i < 30; i++) {
                    int now = 1 << i;
                    if (now > mn[p] && now > v) break;
                    if (mn[p] & now) cnt[i][p] -= mnCnt[p];
                    if (v & now) cnt[i][p] += mnCnt[p];
                }
                if (mnCnt[p] & 1) sum[p] ^= mn[p] ^ v;
                mn[p] = tag[p] = v;
            } else {
                down(p);
                getMax(lc, l, mid, x, y, v), getMax(rc, mid + 1, r, x, y, v);
                up(p);
            }
            return;
        }
        down(p);
        if (x <= mid) getMax(lc, l, mid, x, y, v);
        if (y > mid) getMax(rc, mid + 1, r, x, y, v);
        up(p);
    }

    int qBit(int p, int l, int r, int x, int y, int w) {
        if (x <= l && y >= r) return cnt[w][p];
        down(p);
        int res = 0;
        if (x <= mid) res += qBit(lc, l, mid, x, y, w);
        if (y > mid) res += qBit(rc, mid + 1, r, x, y, w);
        return res;
    }

    int qSum(int p, int l, int r, int x, int y) {
        if (x <= l && y >= r) return sum[p];
        down(p);
        int res = 0;
        if (x <= mid) res ^= qSum(lc, l, mid, x, y);
        if (y > mid) res ^= qSum(rc, mid + 1, r, x, y);
        return res;
    }

} using namespace SegT;

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(n), read(qn);
    for (int i = 1; i <= n; i++) read(a[i]);
    build(1, 1, n);
    for (int opt, x, y, v; qn--; ) {
        read(opt), read(x), read(y), read(v);
        if (opt == 1) {
            getMax(1, 1, n, x, y, v);
        } else {
            // int s = 0;
            // for (int bin = 1, i = 0; i < 30; i++, bin <<= 1) {
            //     int now = qBit(1, 1, n, x, y, i);
            //     if (now & 1) s += bin;
            // }
            int s = qSum(1, 1, n, x, y);
            s ^= v;
            if (s == 0) write(0, '\n');
            else {
                int w = 0;
                for (int tmp = s; tmp != 0; tmp >>= 1, ++w);
                --w;
                int res = qBit(1, 1, n, x, y, w);
                if ((v ^ s) <= v) ++res;
                write(res, '\n');
            }
        }

        // for (int i = 1; i <= n; i++) {
        //     int now = 0;
        //     for (int j = 0; j < 30; j++) now += qBit(1, 1, n, i, i, j) << j;
        //     printf("%d%c", now, " \n"[i == n]);
        // }
    }

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
#endif
    return 0;
}

链接

segment beats 冬令营课件

UOJ的一个讨论帖,jls关于复杂度的声明

CF 上的一篇介绍文,下有题目

posted @ 2021-02-21 15:52  CzxingcHen  阅读(263)  评论(1编辑  收藏  举报