题解: P11071 「QMSOI R1」 Distorted Fate
给定一个长度为 \(n\) 的数组 \(A\),你需要完成以下 \(q\) 次操作。
1.1 l r x
将 \(A_i(l\le i\le r)\) 异或上 \(x\)。
2.2 l r
求:
其中 \(\bigcup\) 表示按位或。
Input
第一行输入两个数 \(n\) 和 \(q\),代表数组长度和操作的次数。
第二行输入 \(n\) 个整数,第 \(i\) 个数代表 \(A_i\) 的值。
接下来 \(q\) 行,每行输入三个整数 \(opt,l,r\) 。
若 \(opt=1\) ,则再输入一个整数 \(x\) 表示将区间 \([l,r]\) 中 \(A_i\) 异或上 \(x\)。
若 \(opt=2\) ,则代表这是一次查询。
Output
对于每个查询,输出一行一个整数代表所求式子的值 \(\bmod \ 2^{30}\) 的结果。
Note
对于所有数据,满足 \(0\le a_i,x<2^{30},1\le l\le r\le n\) 。
空间限制 \(100MB\) 。
分析
很自然往拆位的思路去想。
因为空间限制卡的很死,所以不能直接在线做。
先把操作离线下来,每一位单独做。由于是前缀按位或,相当于对于 \([l,r]\) 的 \(0/1\) 序列,找到第一个出现的 \(1\) 的位置,其后包括它本身都会对答案有贡献。
这样就可以考虑线段树上每个节点维护两个值 \(pos_0\) 和 \(pos_1\) ,建树时如果 \(A_i\) 当前处理的位置上为 \(1\) 就把 \(pos_1\) 赋成 \(i\), \(pos_0\) 赋成极大值;当前位置上为 \(0\) 就反过来。每次处理如果 \(x_i\) 这一位为 \(1\) 就可以直接把 \([l,r]\) 的 \(pos_0, pos_1\) 交换(等价于异或),用 \(tag_u\) 延迟下放标记即可。
每一位操作可以重复建在同一棵线段树上。记得清空标记。
AC代码:
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
const int maxn = 2e5 + 10, mo = 1 << 30, inf = 1 << 30;
int bit, a[maxn], ans[maxn], pos0[maxn << 2], pos1[maxn << 2];
struct REQ{
int op, l, r, x;
}req[maxn];
bool tag[maxn << 2];
void upd(int u) {
pos0[u] = min(pos0[u << 1], pos0[u << 1 | 1]), pos1[u] = min(pos1[u << 1], pos1[u << 1 | 1]);
return;
}
void build(int u, int l, int r) {
if(l == r) {
if((a[l] >> bit) & 1) pos0[u] = inf, pos1[u] = l;
else pos0[u] = l, pos1[u] = inf;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
return upd(u), void(0);
}
void pushdown(int u) {
if(!tag[u]) return;
swap(pos0[u << 1], pos1[u << 1]), swap(pos0[u << 1 | 1], pos1[u << 1 | 1]);
tag[u << 1] ^= 1, tag[u << 1 | 1] ^= 1;
tag[u] = 0; //记得清空tag
return;
}
void Do(int u, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
tag[u] ^= 1;
swap(pos0[u], pos1[u]);
return;
}
pushdown(u);
int mid = l + r >> 1;
if(ql <= mid) Do(u << 1, l, mid, ql, qr);
if(mid < qr) Do(u << 1 | 1, mid + 1, r, ql, qr);
return upd(u), void(0);
}
int ask(int u, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) return pos1[u];
pushdown(u);
int mid = l + r >> 1, ret = inf;
if(ql <= mid) ret = min(ret, ask(u << 1, l, mid, ql, qr));
if(mid < qr) ret = min(ret, ask(u << 1 | 1, mid + 1, r, ql, qr));
return ret;
}
int main() {
int n = read(), q = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= q; i++) {
req[i].op = read(), req[i].l = read(), req[i].r = read();
if(req[i].op == 1) req[i].x = read();
}
for(bit = 0; bit <= 30; bit++) {
memset(tag, 0, sizeof tag);
build(1, 1, n);
for(int i = 1; i <= q; i++) {
if(req[i].op == 1 && (req[i].x >> bit) & 1) Do(1, 1, n, req[i].l, req[i].r);
if(req[i].op == 2) {
int res = ask(1, 1, n, req[i].l, req[i].r);
if(res != inf) ans[i] = (ans[i] + 1ll * (req[i].r - res + 1) * (1 << bit) % mo) % mo;
}
}
}
for(int i = 1; i <= q; i++) {
if(req[i].op == 2) printf("%d\n", ans[i]);
}
return 0;
}
标签:le,R1,NOIP,int,题解,req,pos,inf
From: https://www.cnblogs.com/Ydoc770/p/18536838