#3166. [Heoi2013]Alo

  • description
  • solution
  • code

BZOJ3166

description

Welcome to ALO ( Arithmetic and Logistic Online)。这是一个VR MMORPG ,
如名字所见,到处充满了数学的谜题。
现在你拥有n颗宝石,每颗宝石有一个能量密度,记为ai,这些宝石的能量密度两两不同。现在你可以选取连续的一些宝石(必须多于一个)进行融合,设为 ai, ai+1, …, a j,则融合而成的宝石的能量密度为这些宝石中能量密度的次大值与其他任意一颗宝石的能量密度按位异或的值,即,设该段宝石能量密度次大值为k,则生成的宝石的能量密度为max{k xor ap | ap ≠ k , i ≤ p ≤ j}。
现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。

Input

第一行,一个整数 n,表示宝石个数。
第二行, n个整数,分别表示a1至an,表示每颗宝石的能量密度,保证对于i ≠ j有 ai ≠ aj。

Output

输出一行一个整数,表示最大能生成的宝石能量密度。

Sample Input

5
9 2 1 4 7

Sample Output

14

Hint

选择区间[1,5],最大值为 7 xor 9。

对于 100%的数据有 1 ≤ n ≤ 50000, 0 ≤ ai ≤ 10^9

solution

首先肯定是枚举每一个宝石的aia_iai​作为次大值

区间[l,r][l,r][l,r]内与xxx异或的最大值,就是可持久化trie树

对于iii版本的trie树记录的是[1,i][1,i][1,i]的所有宝石信息

但是本题的难点是在于求出每个宝石做次大值时,有效的区间范围

定义前驱的含义:宝石密度大于aia_iai​的在iii前面的距iii最近的宝石

定义后继的含义:宝石密度大于aia_iai​的在iii后面的距iii最近的宝石

对于每个iii,询问的合法区间其实是前驱的前驱位置l加一,后继的后继位置r减一

  • 这里就有问题了,看似好像包含了两个比aia_iai​大的宝石,iii宝石不再是次大值宝石

    • 其实并不是,要求包含前驱后继并不是意味着在(l,r)(l,r)(l,r)这个区间进行询问

      这个区间当然iii宝石是次次大值

    • 这是意味着两个独立区间的问题(l,i];[i,r)(l,i];[i,r)(l,i];[i,r)

      这么说的原因是,假设ai⨁aj(l<j<i<r)a_i\bigoplus a_j(l<j<i<r)ai​⨁aj​(l<j<i<r)是最大值,那么相当于选择的区间是(l,i](l,i](l,i]。

      同理假设ai⨁aj(l<i<j<r)a_i\bigoplus a_j(l<i<j<r)ai​⨁aj​(l<i<j<r)是最大值,那么相当于选择的区间是[i,r)[i,r)[i,r)

      这样aia_iai​在两个区间就扮演着次大值的角色了

    • 如果只是对于一个iii考虑前驱的位置加一到后继的后继位置减一,其实只是考虑了最大值在次大值aia_iai​后面的区间,忽略了最大值在aia_iai​前面的区间

    • 如果只是对于一个iii考虑前驱的前驱位置加一到后继的位置减一,其实只是考虑了最大值在次大值aia_iai​前面的区间,忽略了最大值在aia_iai​后面的区间

知道了选取区间端点的条件,代码怎么找呢?

  • set

具体而言:将宝石按照密度排序,按密度从大到小地加入宝石,set里面放的是宝石的下标iii,那么此时set里面的所有宝石都大于等于现在这个刚加进去的宝石密度

为了能阐释得更清楚,我们直接解读代码

首先是寻找后继的后继的位置,减一会在主函数的查询调用时减掉,这里暂时不考虑

  • 这里两个if语句的先后顺序是先让迭代器it自加后,再判断是否指向了setend()位置

  • 众所周知set是左闭右开的,end()位置是最后一个值的位置的下一个空位置

  • 先自加一,指向后继;再自加一,指向后继的后继

  • int find_r( int x ) {auto it = s.find( x );if( ++ it == s.end() ) return n + 1;if( ++ it == s.end() ) return n + 1;return *it;
    }
    

然后看寻找前驱的前驱代码,同样的加一操作在主函数的询问完成,这里不进行加一

  • 这里两个if语句的先后顺序是,先判断是否指向了begin()位置,再自减一

  • 第一个if如果成立,意味着当前加入的宝石下标最小,前驱都没有

  • 否则,迭代器就指向了前驱

  • 再到第二个if语句,如果成立,意味着前驱就是下标最小的宝石了,前驱的前驱没有

  • 否则迭代器就指向了前驱的前驱,达到目的

int find_l( int x ) {auto it = s.find( x );if( it -- == s.begin() ) return 0;if( it -- == s.begin() ) return 0;return *it;
}

最后还有一个代码细节

在插入操作和查询操作中,涉及到最后一层二进制位000的处理

void insert( int &now, int lst, int x, int d ) {if( d < 0 ) return;t[now = ++ cnt] = t[lst];t[now].sum ++;int k = x >> d & 1;insert( t[now].son[k], t[lst].son[k], x, d - 1 );
}int query( int l, int r, int x, int d ) {if( t[r].sum - t[l].sum == 0 ) return 0;if( d == 0 ) return 1;int k = x >> d & 1;if( t[t[r].son[k ^ 1]].sum - t[t[l].son[k ^ 1]].sum )return query( t[l].son[k ^ 1], t[r].son[k ^ 1], x, d - 1 ) + ( 1 << d );elsereturn query( t[l].son[k], t[r].son[k], x, d - 1 );
}

如果不在query操作设置if( d == 0 ) return 1的这个出口,在insert的时候就必须把二进制位−1-1−1建出来,即

void insert( int &now, int lst, int x, int d ) {t[now = ++ cnt] = t[lst];t[now].sum ++;if( d < 0 ) return;int k = x >> d & 1;insert( t[now].son[k], t[lst].son[k], x, d - 1 );
}int query( int l, int r, int x, int d ) {if( d < 0 ) return 0;if( t[r].sum - t[l].sum == 0 ) return 0;int k = x >> d & 1;if( t[t[r].son[k ^ 1]].sum - t[t[l].son[k ^ 1]].sum )return query( t[l].son[k ^ 1], t[r].son[k ^ 1], x, d - 1 ) + ( 1 << d );elsereturn query( t[l].son[k], t[r].son[k], x, d - 1 );
}

为什么呢?

  • 如果不在查询时设置最底层叶子的出口,那么就会进入对叶子左右儿子的if判断
  • 然而,插入操作又只在最底层叶子建点后就返回了
  • 所以叶子节点的儿子没有被分配过,贸然访问,完全不知道计算机会找到哪儿去
  • 我就是这个顺序细节问题,一直比答案少一

code

#include <set>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 50005
set < int > s;
pair < int, int > a[maxn];
int n, cnt;
int root[maxn];
struct { int son[2], sum; } t[maxn * 35];void insert( int &now, int lst, int x, int d ) {if( d < 0 ) return;t[now = ++ cnt] = t[lst];t[now].sum ++;int k = x >> d & 1;insert( t[now].son[k], t[lst].son[k], x, d - 1 );
}int query( int l, int r, int x, int d ) {if( t[r].sum - t[l].sum == 0 ) return 0;if( d == 0 ) return 1;int k = x >> d & 1;if( t[t[r].son[k ^ 1]].sum - t[t[l].son[k ^ 1]].sum )return query( t[l].son[k ^ 1], t[r].son[k ^ 1], x, d - 1 ) + ( 1 << d );elsereturn query( t[l].son[k], t[r].son[k], x, d - 1 );
}int find_l( int x ) {auto it = s.find( x );if( it -- == s.begin() ) return 0;if( it -- == s.begin() ) return 0;return *it;
}int find_r( int x ) {auto it = s.find( x );if( ++ it == s.end() ) return n + 1;if( ++ it == s.end() ) return n + 1;return *it;
}int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &a[i].first );a[i].second = i;insert( root[i], root[i - 1], a[i].first, 30 );}sort( a + 1, a + n + 1 );int ans = 0;s.insert( a[n].second );for( int i = n - 1;i;i -- ) {s.insert( a[i].second );int l = find_l( a[i].second );int r = find_r( a[i].second );ans = max( ans, query( root[l], root[r - 1], a[i].first, 30 ) );}printf( "%d\n", ans );return 0;
}

BZOJ #3166. [Heoi2013]Alo(可持久化trie树+set)相关推荐

  1. Bzoj 3166 [Heoi2013] Alo 题解

    3166: [Heoi2013]Alo Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 1118  Solved: 518 [Submit][Stat ...

  2. BZOJ 3261 最大异或和 可持久化Trie树

    题目大意:给定一个序列,提供下列操作: 1.在数组结尾插入一个数 2.给定l,r,x,求一个l<=p<=r,使x^a[p]^a[p+1]^...^a[n]最大 首先我们能够维护前缀和 然后 ...

  3. 【bzoj3261】最大异或和 可持久化Trie树

    题目描述 给定一个非负整数序列 {a},初始长度为 N.        有M个操作,有以下两种操作类型: 1.A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+1. 2.Q l r x: ...

  4. P4735 最大异或和(可持久化trie树、求最大区间异或和)

    P4735 最大异或和 我们维护一个前缀异或和:s[i]=a[1]xora[2]xor-a[i−1]xora[i]s[i] = a[1] \ xor\ a[2]\ xor\ - a[i-1] \ xo ...

  5. BZOJ3261 最大异或和 解题报告(可持久化Trie树)

    本题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3261 题目描述 给定一个非负整数序列{a},初始长度为N. 有M个操作,有以下两种操作类 ...

  6. HDU - 4757 Tree(LCA+可持久化trie树)

    题目链接:点击查看 题目大意:给出一棵有有n个节点的树,每个点都有一个权值,现在给出m个查询,每次查询的形式为:x,y,z,求出从点x到点y的路径上任选一点,使其与z的异或值最大,输出异或值 题目分析 ...

  7. BZOJ3261: 最大异或和(可持久化trie树)

    题意 题目链接 Sol 设\(sum[i]\)表示\(1 - i\)的异或和 首先把每个询问的\(x \oplus sum[n]\)就变成了询问前缀最大值 可持久化Trie树维护前缀xor,建树的时候 ...

  8. BZOJ3166 [Heoi2013]Alo 【可持久化trie树 + 二分 + ST表】

    题目 Welcome to ALO ( Arithmetic and Logistic Online).这是一个VR MMORPG , 如名字所见,到处充满了数学的谜题. 现在你拥有n颗宝石,每颗宝石 ...

  9. 省选专练(学习)可持久化Trie树(BZOJ3261)

    这个似乎也不是好难啊 但是可持久化Trie还是可以干许多线性基不能干的事. 什么是可持久化Trie? 顾名思义:是一种可以持久化的Trie树 他的建树方式和键值式线段树方式类似 也支持版本的减法 查询 ...

最新文章

  1. JQuery 动态创建表单,并自动提交
  2. PostgreSQL 模式删除背后的代码
  3. 用 Fiddler 来弥补 Chrome Network 的小缺点
  4. Java中final和static对修饰类、方法、属性的总结
  5. 操作系统-信号量的使用
  6. showmodaldialog 为什么不能复制_防复制的门禁读头可以防止UID和FUID读卡器
  7. SQL点滴系列之删除数据(五)
  8. 3mysql的引擎哪_你知道哪几种MySQL存储引擎?
  9. kinect 手势识别的原理?
  10. 串口.Qt532测试(异步)
  11. Androidx 切换多语言失效解决方案(appcompat版本有关)
  12. 模2运算_模二除法和CRC循环冗余校验
  13. C盘空间不够?教你简单扩容C盘空间
  14. 想查看实时卫星影像?最近一周就不错了
  15. linux getenv函数 get,linux之getenv putenv setenv和unsetenv详解
  16. 千氪公开课 | 自媒体下半场,如何把握区块链写作的红利?
  17. Android出海攻略(一):Google Play 上架扫盲
  18. 山天大畜 (易經大意 韓長庚)
  19. 我的架构梦:(九十九)消息中间件之RocketMQ的高可用机制——消息消费高可用
  20. 中国风PPT不会做?三个小技巧帮你搞定!!!

热门文章

  1. 2018年最后一个月最值得关注的13个优质公号
  2. 北大清华团队编写!200多个科学实验+视频,和爸爸一起在家做
  3. 【10.29周一电商,已好】中国日历的至高境界,377张震撼级插画,美到爆!
  4. 计算机网络就业范围分析,计算机网络技术专业就业前景怎么样「就业形势分析」...
  5. java merge css_一句命令快速合并 JS、CSS
  6. html怎么快速打出来的,javascript – 快速打印HTML5画布
  7. 传递函数_使用python计算麦克风阵列信号的传递函数
  8. php-fpm 超时,PHP超时的坑
  9. c语言字符串倒置,单词倒置,用C++实现,将一句话里的单词进行倒置的方法详解
  10. 访问Web服务器时 使用的协议是,使用SOAP协议访问Web服务