[BZOJ3166][Heoi2013]Alo
[BZOJ3166][Heoi2013]Alo
试题描述
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}。
现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。
输入
第一行,一个整数 n,表示宝石个数。
第二行, n个整数,分别表示a1至an,表示每颗宝石的能量密度,保证对于i ≠ j有 ai ≠ aj。
输出
输出一行一个整数,表示最大能生成的宝石能量密度。
输入示例
5 9 2 1 4 7
输出示例
14
数据规模及约定
对于 100%的数据有 1 ≤ n ≤ 50000, 0 ≤ ai ≤ 10^9
题解
首先我们可以假定每个数是次大值,然后求出满足条件的极大区间,可以证明这样极大的区间最多有两个。这个极大区间的求法可以用 RMQ + 二分来做。
那么现在问题变成了:给你一个区间 [l, r],再给你一个数字 A,询问区间 [l, r] 中与 A 异或的最大值是多少。求异或最大值的问题一般都是在 trie 上贪心嘛,但是这里有一个区间的限制,我们就可以使用可持久化 trie 来做,非常类似主席树,每次从上一个版本继承下来并在此基础上添加一条链(记得维护一下子树 siz 大小);查询的时候看一下版本 r 的 siz - 版本 l 的 siz 是否为 0 就可以判断当前节点是不是版本 [l, r] 之间的了。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 50010 #define maxnode 1550010 #define maxlog 16 #define oo 2147483647 int n, A[maxn], pre[maxn], nxt[maxn]; int mxa[maxlog][maxn], Log[maxn]; void rmq_init() { Log[1] = 0; for(int i = 2; i <= n; i++) Log[i] = Log[i>>1] + 1; for(int i = 1; i <= n; i++) mxa[0][i] = A[i]; for(int j = 1; (1 << j) <= n; j++) for(int i = 1; i + (1 << j) - 1 <= n; i++) mxa[j][i] = max(mxa[j-1][i], mxa[j-1][i+(1<<j-1)]); return ; } int que(int l, int r) { if(l > r) return 0; if(l < 1 || r > n) return oo; int t = Log[r-l+1]; return max(mxa[t][l], mxa[t][r-(1<<t)+1]); } int ToT, rt[maxn], ch[maxnode][2], siz[maxnode]; int num[35]; void update(int& y, int x, int p) { siz[y = ++ToT] = 1; if(!p) return ; memcpy(ch[y], ch[x], sizeof(ch[x])); update(ch[y][num[p]], ch[x][num[p]], p - 1); siz[y] += siz[ch[y][0]] + siz[ch[y][1]]; return ; } int query(int lu, int ru, int p, int sum) { if(!p) return sum; if(siz[ch[ru][num[p]^1]] - siz[ch[lu][num[p]^1]]) return query(ch[lu][num[p]^1], ch[ru][num[p]^1], p - 1, sum << 1 | 1); return query(ch[lu][num[p]], ch[ru][num[p]], p - 1, sum << 1); } int main() { n = read(); for(int i = 1; i <= n; i++) A[i] = read(); A[0] = A[n+1] = oo; for(int i = 1; i <= n; i++) { if(A[i-1] > A[i]) pre[i] = i - 1; else { pre[i] = pre[i-1]; while(A[pre[i]] < A[i]) pre[i] = pre[pre[i]]; } } for(int i = n; i; i--) { if(A[i+1] > A[i]) nxt[i] = i + 1; else { nxt[i] = nxt[i+1]; while(A[nxt[i]] < A[i]) nxt[i] = nxt[nxt[i]]; } } for(int i = 1; i <= n; i++) { int cnt = 0; memset(num, 0, sizeof(num)); int tmp = A[i]; while(tmp) num[++cnt] = tmp & 1, tmp >>= 1; update(rt[i], rt[i-1], 30); } rmq_init(); int ans = 0; for(int i = 1; i <= n; i++) { // printf("fir: [%d, %d]\n", pre[i], nxt[i]); int ToLeft, ToRight; int l = 0, r = pre[i]; while(l < r) { int mid = l + r >> 1; if(que(mid, pre[i] - 1) > A[i]) l = mid + 1; else r = mid; } ToLeft = l; l = nxt[i]; r = n + 1; while(r - l > 1) { int mid = l + r >> 1; if(que(nxt[i] + 1, mid) > A[i]) r = mid; else l = mid; } ToRight = min(l, n + 1); // printf("%d(%d): [%d, %d]\n", i, A[i], ToLeft, ToRight); if(ToRight <= n) { int cnt = 0; memset(num, 0, sizeof(num)); int tmp = A[i]; while(tmp) num[++cnt] = tmp & 1, tmp >>= 1; ans = max(ans, query(rt[pre[i]], rt[ToRight], 30, 0)); } if(ToLeft) { int cnt = 0; memset(num, 0, sizeof(num)); int tmp = A[i]; while(tmp) num[++cnt] = tmp & 1, tmp >>= 1; ans = max(ans, query(rt[ToLeft-1], rt[nxt[i]-1], 30, 0)); } } printf("%d\n", ans); return 0; }