校内集训11.1小结


前言:这次做了一套提高难度的题,总分反而比上次高了。这次考试除了第二题没想出单调栈有些可惜外,也没有什么遗憾了。为了增强本人的考场思维能力,特地写一篇来总结一下。

T1:腿部挂件

题目描述
Jim是一个热爱打游戏的小伙子,可惜他的游戏水平不太行,以至于经常在游戏里被别人欺负。而且Jim不仅游戏玩的菜,他还很爱喷人,但是由于自己的垃圾操作,他又喷不过别人。为了改善这种局面,Jim决定成为一个腿部挂件(俗称抱大腿)。
已知现在有N个选手可供Jim选择,每位选手的能力值为 ai。,N位选手不一定每位选手都有时间陪Jim玩游戏而且Jim的状态也时好时坏,所以Jim有Q个询问,每个询问是3个数X、L、R,求第L个人到第R个人这R-L+1个人中与Jim配合后的能力值最大是多少,Jim 与第i位选手配合后的能力值为ai与X进行异或运算(Xor)。

输入
第1行:2个数N, Q中间用空格分隔,分别表示选手个数及查询的数量(1≤N≤200000,
1≤Q≤200000)。
第2 行:共N个数为每个选手能力值中间用空格分隔,对应ai(0≤a[i]≤10^9)。
第N+2 - N+Q+1行:每行3个数X, L, R,中间用空格分隔。(0≤X≤10^9,0≤L≤R<N)

输出
输出共Q行,对应每次询问所能得到的最大能力值。

样例输入

9 8
2 15 4 12 0 6 0 16 12
2 2 5
4 8 8
16 5 8
16 6 8
1 0 5
12 3 4
15 1 1
5 0 4

样例输出

14
8
28
28
14
12
0
10

提示
样例解释
对于第一个询问 2 与{4 12 0} 最大的能力值组合为 2 xor 12 = 14 注意 L R标号从 0 开始
对于20%的数据保证 (1≤N≤5000, 1≤Q≤5000)。
对于45%的数据保证 (1≤N≤50000, 1≤Q≤50000)。
对于100%的数据保证 (1≤N≤200000, 1≤Q≤200000)。

简要思路:这题需要用可持久化01Trie,将N个选手的能力值拆成二进制并将位数从高到低存在Trie中(有个不计数的根节点)。对于当前的X值,最大异或值就是根据X的二进制表示来尽可能地走反边,因为位数从高到低,所以在前面走反边肯定更优。
我们用cnt记录当前节点数来判定走反边是否可行,因为涉及区间操作,要用到前缀和,在预处理时要将上一棵树各节点的计数传到这一棵树上,代码实现如下:

inline void update( int & root , int dep , int num ) {
   
	int last = root;
	seg[root = ++scnt] = seg[last];//加上前面的结果
	++seg[root].cnt;
	if ( dep == -1 ) {
   
		return;
	}
	update( seg[root].son[( num >> dep ) & 1] , dep - 1 , num ); 
}

在main函数中:

for ( int i = 1 ; i <= n ; ++i ) {
   
		read(num);//快读
		root[i] = root[i - 1];//记录存贮每个选手能力值的树的根节点
		update( root[i] , maxd - 1 , num );
	}

如果觉得不好理解,那就画幅图吧,假设我们先后插入3,2,1,3(为了方便,深度设为3)。
图丑请见谅qwq
图片
根节点的计数意义不大,不用理会。本图中未标明的点就是计数为零的点。注意到,本图画出了在构建Trie树时我们根本没涉及到的点,在查询时如果访问到这些点,会自动进入零点,这对前缀和判断可行性没有影响,还节省了空间。
如果还有不懂的,那我只好上代码了。

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 200005
#define maxd 30
using namespace std;
int n , q , scnt , root[N];
struct Trie{
   
	int cnt;
	int son[2];
}seg[N * (maxd + 1)];
template < typename T >
inline void read( T & res ) {
   
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
   
		if ( aa == '-' ) {
   
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
   
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
inline void update( int & root , int dep , int num ) {
   
	int last = root;
	seg[root = ++scnt] = seg[last];
	++seg[root].cnt;
	if ( dep == -1 ) {
   
		return;
	}
	update( seg[root].son[( num >> dep ) & 1] , dep - 1 , num );
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值