CF1100F Ivan and Burgers 题解

CF1100F Ivan and Burgers

题面:

题面翻译

给一个长为 n n n 的序列, q q q 次询问,问从 a l , a l + 1 , ⋯   , a r a_l,a_{l+1},\cdots,a_r al,al+1,,ar 中选若干个数,异或和最大为多少。

1 ≤ n , q ≤ 5 × 1 0 5 1\le n,q\le 5\times 10^5 1n,q5×105 0 ≤ c i ≤ 1 0 6 0\le c_i\le 10^6 0ci106

样例 #1
样例输入 #1
4
7 2 3 4
3
1 4
2 3
1 3
样例输出 #1
7
3
7
样例 #2
样例输入 #2
5
12 14 23 13 7
15
1 1
1 2
1 3
1 4
1 5
2 2
2 3
2 4
2 5
3 3
3 4
3 5
4 4
4 5
5 5
样例输出 #2
12
14
27
27
31
14
25
26
30
23
26
29
13
13
7

区间查询最大异或和(前缀和线性基)

类似于 可持久化线段树,读者可以类比

我们需要记录每个数位被数占领的时间和数值,

我们插入一个数,就建立一个新的版本,其的内容是复制上一个版本(这里是完全赋值,不是可持久化线段树的直接接上),

然后我们考虑插入数,如果有数位可以填入,就填入;如果有的数位填入的时间太早,我们考虑用这个数替换,然后用被替换的数继续做线性基

为什么要替换呢?因为我们记录每个数占领的时间,当我们询问 ( l , r ) (l,r) (l,r) 时,对于版本 r r r,有效的数需要满足 t i m e i ≥ l time_{i} \geq l timeil

而这个位置不考虑插入时间时,可能有多个数字可以胜任,但是考虑插入时间后,其中能胜任的只有 t i m e i ≥ l time_{i} \geq l timeil,其中最保险的自然是 t i m e i max ⁡ time_{i\max} timeimax

如果 t i m e i max ⁡ time_{i\max} timeimax 都无法胜任,那么 插入时间为 r r r 之前的没有数字可以胜任,这样构造的答案一定合法。

大体思路就是这样,结合代码再思考应该就能理解。

AC-code:

#include<bits/stdc++.h>
using namespace std;
		
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
const int N = 5e5 + 5;
int p[N][31],pos[N][31],n,m;

void insert(int x,int id) {
	for(int i = 0;i<=30;i++) {
		p[id][i] = p[id - 1][i];
		pos[id][i] = pos[id - 1][i];
	}
	int P = id;
	for(int i = 30;i >= 0;i--) {
		if(x >> i & 1) {
			// if(!p[id][i])  {
			// 	p[id][i] = x;
			// 	pos[id][i] = P;
			// 	break;
			// }
			if(pos[id][i] < P) 
				swap(p[id][i],x),swap(pos[id][i],P);
			x ^= p[id][i];
		}
	}
}

int query(int l,int r) {
	int ans = 0;
	for(int i = 30;i >= 0;i--) {
		if(pos[r][i] >= l) 
			ans = max(ans,ans ^ p[r][i]);
	}
	return ans;
}

signed main() {
	n = rd();
	for(int i = 1;i<=n;i++) {
		int x = rd();
		insert(x,i);
	}
	m = rd();
	while(m--) {
		int l = rd(),r = rd();
		wt(query(l,r));putchar('\n');
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值