http://lightoj.com/volume_showproblem.php?problem=1269
题意:给定一个序列,求选定一段区间的亦或和最大值和最小值。
由于是区间问题,很容易就能想到是要先求前缀异或和。
可以对所有前缀和建立字典树,节点的末尾保存二进制转化成十进制的值。
先查询,再插入。
可以先将所有的的数字转化成二进制字符串,高位在前,低位在后。
最大值很简单,查找时然后尽量往反向走;
最小则需要尽量往正向走。
知道走到尽头时把找到的十进制,异或上当前的值。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 50005;
const int maxnode = MAXN * 33;
const int sigma_size = 2;
const int INF = (1LL << 31) - 1;
struct Trie {
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); }
Trie() { clear(); }
int idx(char c) { return c - '0'; }
void insert(char *s, int v) {
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++) {
int c = idx(s[i]);
if(!ch[u][c]) {
memset(ch[sz], 0, sizeof(ch[sz]));
val[u] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = v;
}
//^0 equal
//^1 opposite
int find(char *s, int value, int dir) {
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++) {
int c = idx(s[i]);
int v = c ^ dir;
if(!ch[u][v])
v ^= 1;
u = ch[u][v];
}
return val[u] ^ value;
}
} trie;
int n, sum[MAXN], A[MAXN];
char bit[33];
void trans(char *s, int v) {
for(int i = 31; i >= 0; i--)
s[i] = ((v >> i) & 1) + '0';
s[32] = '\0';
reverse(s, s+32);
}
int main() {
int T, cas = 1;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
trie.clear();
sum[0] = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &A[i]);
sum[i] = sum[i-1] ^ A[i];
}
trans(bit, 0);
trie.insert(bit, 0);
int maxv = -INF, minv = INF;
int mx, mi;
for(int i = 1; i <= n; i++) {
trans(bit, sum[i]);
mi = trie.find(bit, sum[i], 0);
mx = trie.find(bit, sum[i], 1);
minv = min(mi, minv);
maxv = max(mx, maxv);
trie.insert(bit, sum[i]);
}
printf("Case %d: %d %d\n", cas++ , maxv, minv);
}
return 0;
}