BZOJ 3166: [Heoi2013]Alo

3166: [Heoi2013]Alo

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 923  Solved: 437
[Submit][Status][Discuss]

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

Source

加强型数据By Hta

[Submit][Status][Discuss]

首先,应该枚举选择哪个数字作为次大值,然后需要知道其对应的可以选取哪个范围内的数字作为题目中的$ap$。

范围可以通过预处理得到,方法是先用二分得到每个数左侧第一个大于这个数的地方,然后再二分出第二个大于这个数的地方。每个数到两侧第二个大于这个数的范围就是$ap$的可选范围,不含第二个大于这个数的数字。

然后,问题转化为求一个数字在一个区间内的最大异或数字,这个问题是经典的Trie树问题,尽量“反着跑”即可。但是有区间限制,并且是n组询问,所以可以用大佬的可持久化Trie或我这种蒟蒻的莫队+Trie解决。

  1 #include <bits/stdc++.h>
  2
  3 const int siz = 50005;
  4
  5 int n, num[siz];
  6
  7 int st_maxi[siz][17];
  8
  9 inline void preworkST(void)
 10 {
 11     for (int i = 1; i <= n; ++i)
 12         st_maxi[i][0] = num[i];
 13
 14     for (int i = 1; i < 17; ++i)
 15         for (int j = 1; j <= n; ++j)
 16             if (j + (1 << i) - 1 <= n)
 17                 st_maxi[j][i] = std::max(
 18                     st_maxi[j][i - 1],
 19                     st_maxi[j + (1 << (i - 1))][i - 1]);
 20 }
 21
 22 inline int stMax(int l, int r)
 23 {
 24     if (l > r)return -1;
 25
 26     int len = r - l + 1, log = 0;
 27
 28     while (len >= (1 << (log + 1)))++log;
 29
 30     return std::max(
 31         st_maxi[l][log],
 32         st_maxi[r - (1 << log) + 1][log]);
 33 }
 34
 35 int nt_pre[siz];
 36 int nt_nxt[siz];
 37
 38 inline void preworkNT(void)
 39 {
 40     for (int i = 1; i <= n; ++i)
 41     {
 42         int val = num[i], lt = 1, rt = i, mid, pos = 1, ans = 1;
 43
 44         while (lt <= rt)
 45         {
 46             mid = (lt + rt) >> 1;
 47
 48             if (stMax(mid, i) > val)
 49                 lt = mid + 1, pos = mid;
 50             else
 51                 rt = mid - 1;
 52         }
 53
 54         lt = 1, rt = pos - 1;
 55
 56         while (lt <= rt)
 57         {
 58             mid = (lt + rt) >> 1;
 59
 60             if (stMax(mid, pos - 1) > val)
 61                 lt = mid + 1, ans = mid;
 62             else
 63                 rt = mid - 1;
 64         }
 65
 66         nt_pre[i] = ans;
 67     }
 68
 69     for (int i = 1; i <= n; ++i)
 70     {
 71         int val = num[i], lt = i, rt = n, mid, pos = n, ans = n;
 72
 73         while (lt <= rt)
 74         {
 75             mid = (lt + rt) >> 1;
 76
 77             if (stMax(i, mid) > val)
 78                 rt = mid - 1, pos = mid;
 79             else
 80                 lt = mid + 1;
 81         }
 82
 83         lt = pos + 1, rt = n;
 84
 85         while (lt <= rt)
 86         {
 87             mid = (lt + rt) >> 1;
 88
 89             if (stMax(pos + 1, mid) > val)
 90                 rt = mid - 1, ans = mid;
 91             else
 92                 lt = mid + 1;
 93         }
 94
 95         nt_nxt[i] = ans;
 96     }
 97 }
 98
 99 struct query {
100     int l, r, t, ans;
101 }q[siz];
102
103 int s;
104
105 inline bool cmp(const query &a, const query &b)
106 {
107     if (a.l / s != b.l / s)
108         return a.l < b.l;
109     else
110         return a.r < b.r;
111 }
112
113 const int tri = 5000005;
114
115 int next[tri][2], sum[tri], tot = 1;
116
117 inline void insert(int t)
118 {
119     int p = 1;
120
121     for (int i = 30; i >= 0; --i)
122     {
123         int c = (t >> i) & 1;
124
125         if (!next[p][c])
126             next[p][c] = ++tot;
127
128         p = next[p][c];
129
130         ++sum[p];
131     }
132 }
133
134 inline void remove(int t)
135 {
136     int p = 1;
137
138     for (int i = 30; i >= 0; --i)
139     {
140         int c = (t >> i) & 1;
141
142         if (!next[p][c])
143             next[p][c] = ++tot;
144
145         p = next[p][c];
146
147         --sum[p];
148     }
149 }
150
151 inline int query(int t)
152 {
153     int ret = 0, p = 1;
154
155     for (int i = 30; i >= 0; --i)
156     {
157         int c = (t >> i) & 1;
158
159         if (sum[next[p][c^1]])
160             p = next[p][c^1], ret |= (1 << i);
161         else if (sum[next[p][c]])
162             p = next[p][c];
163         else
164             return 0;
165     }
166
167     return ret;
168 }
169
170 signed main(void)
171 {
172     scanf("%d", &n);
173
174     for (int i = 1; i <= n; ++i)
175         scanf("%d", num + i);
176
177     preworkST();
178
179     preworkNT();
180
181     for (int i = 1; i <= n; ++i)
182     {
183         q[i].l = nt_pre[i];
184         q[i].r = nt_nxt[i];
185         q[i].t = num[i];
186         q[i].ans = 0;
187     }
188
189     s = sqrt(n);
190
191     std::sort(q + 1, q + 1 + n, cmp);
192
193     int lt = 1, rt = 0, maxi = stMax(1, n);
194
195     for (int i = 1; i <= n; ++i)
196     {
197         while (lt < q[i].l)remove(num[lt++]);
198         while (lt > q[i].l)insert(num[--lt]);
199         while (rt > q[i].r)remove(num[rt--]);
200         while (rt < q[i].r)insert(num[++rt]);
201         if (q[i].t != maxi)q[i].ans = query(q[i].t);
202     }
203
204     int answer = 0;
205
206     for (int i = 1; i <= n; ++i)
207         answer = std::max(answer, q[i].ans);
208
209     printf("%d\n", answer);
210 }

@Author: YouSiki

时间: 2024-10-05 04:33:49

BZOJ 3166: [Heoi2013]Alo的相关文章

BZOJ 3166 HEOI2013 ALO 可持久化trie+st表

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3166(洛谷上也有) 题意概述: 给出一个序列,对于一个区间,其权值为区间中的次大值亦或区间中任意一个数的结果的最大值.求区间权值的最大值. 分析: 考虑每个点作为区间次大的状态,发现对于每个点至多有两个最长区间其为次大值(为了让异或结果最大当然是区间越长越好,选择最多),用二分+静态RMQ算出这两个区间再在可持久化trie上面贪心即可. 论如何现场yy可持久化数据结构23333(基于可

BZOJ 3166 HEOI2013 Alo 可持久化Trie树

题目大意:给定一个不重复的序列a,在a中任选一个区间,求区间内的次大值与区间内的任意一个其它数的最大的异或值 首先我们枚举次大值 对于一个次大值 它可能选择的另一个数的取值范围为(l,r) 其中l为这个数左侧第二个比它大的数 r为这个数右侧第二个比它大的数 在这个区间内的Trie树中贪心寻找最大值即可 这个区间怎么求呢?我们维护一棵平衡树 将数从大到小将下标加进平衡树 每加进一个下标 比它大的数的下标都在平衡树中 求两次后继就是r 求两次前驱就是l 我偷懒写了set-- #include<set

BZOJ 3166 Alo

处理出每个数最靠近它的左右两个比它大的数. 然后可持久化trie. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 200050 #define inf 1000000007 using namespace std; struct num { int val,id; }p[maxn]; int n,a[maxn]; int seg_l

[BZOJ 3166]Alo 可持久化01Trie

这道题入门了 可持久化01Trie 可持久化Trie多数用来解决区间异或k值之类的操作,主要是由高位到低位按位贪心就可以了 其实和主席树是一样的,Trie本身也有前缀相减性,至于空间,动态开点就可以了. 当然我们需要记录每个节点的size 对于这道题,我们可以用线段树处理出每一个数作为次大值的区间,然后去区间的Trie里面查询异或最大值就可以了 #include<iostream> #include<cstdio> #include<cstring> #define p

BZOJ3166: [Heoi2013]Alo

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3166 按权值从大到小排序然后倒序插入set,set维护每个点的位置,然后区间就是前驱的前驱+1到后继的后继-1 . 然后维护一个可持久化trie就可以了. #include<cstring> #include<iostream> #include<cstdio> #include<algorithm> #include<set> #defin

【bzoj3166】[Heoi2013]Alo 可持久化Trie树+STL-set

题目描述 Welcome to ALO ( Arithmetic and Logistic Online).这是一个VR MMORPG ,如名字所见,到处充满了数学的谜题.现在你拥有n颗宝石,每颗宝石有一个能量密度,记为ai,这些宝石的能量密度两两不同.现在你可以选取连续的一些宝石(必须多于一个)进行融合,设为  ai, ai+1, …, a j,则融合而成的宝石的能量密度为这些宝石中能量密度的次大值与其他任意一颗宝石的能量密度按位异或的值,即,设该段宝石能量密度次大值为k,则生成的宝石的能量密

BZOJ 3165: [Heoi2013]Segment

3165: [Heoi2013]Segment Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 465  Solved: 187[Submit][Status][Discuss] Description 要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第i条被插入的线段的标号为i. 2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号. Input 第一行一个整数n,表示共n 个操作. 接下来n行,每行第一

Bzoj 3165 [Heoi2013]Segment题解

3165: [Heoi2013]Segment Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 668  Solved: 276[Submit][Status][Discuss] Description 要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第i条被插入的线段的标号为i. 2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号. Input 第一行一个整数n,表示共n 个操作. 接下来n行,每行第一

bzoj 3165: [Heoi2013]Segment 动态凸壳

3165: [Heoi2013]Segment Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 202  Solved: 89[Submit][Status] Description 要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第i条被插入的线段的标号为i.   2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号. Input 第一行一个整数n,表示共n 个操作. 接下来n行,每行第一个数为0或1.