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;
}