NOIP集训 P11071 「QMSOI R1」 Distorted Fate 题解

对本题的评价:有思维含量的线段树好题。曲子好听,曲绘好看,曲师人品好,谱子写得好,鸠好看

题解: 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求:

\[(\sum_{i=l}^r\bigcup_{j=l}^iA_j ) \bmod 2^{30} \]

其中 \(\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\) ,分别表示值域内第一次出现 \(0\)\(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;
}
posted @ 2024-11-09 17:19  Ydoc770  阅读(48)  评论(0)    收藏  举报