/**
Problem H MathematicalGame
题意:给出一个序列,让你寻找一个区间[L, R]使得序列在区间
[L, R]的异或值最大如果有多个则输出字典序最小的
思路:设sum(x)为区间[1, x]的异或和,要使得区间[L, R]的异或值
最大,即求出sum(R)^sum(L - 1)最大
首先sum(x)为O(n)时间,并且用一个数组记录值为sum的最小下标,
即index[sum(x)] = x; 对每个sum(x)拆成20位的二进制数,建立一
棵树,建树方法如下:根节点为0,从高位往低位扫描,如果扫描为
的数位为0,则新建一个左孩子(否则新建一个右孩子),当然有左(右)
孩子的时候不建, 例如5(00000000000000000101),建树过程就为:
左左左左左左左左左左左左左左左左左左右左右,并在根节点设置
index[5] = x (sum(x)=5且x最小)
然后开始扫描sum(i), 需要找到一个sum(j)使得sum(j) ^ sum(i)最大
如果j < i则区间为[j + 1, i], 否则为[i + 1, j]
假设要搜索的sum(j)为ans, 对于sum(i)的搜索方法如下:
假设sum(i) = 01010000000000001010(二进制), 从根节点开始搜索
先看第一位,因为为0,就看他是否有右孩子,如果有,则进入右孩子,
ans=ans*2+1,理由是因为异或要两个数位不同才会为1,高位为1则越大
否则进入他的左孩子,ans = ans*2(一定有左孩子,因为建树过程中能
到达的非叶子节点一定有右孩子,叶节点代表存在的值,就是说所有的
叶节点深度为21)
对于第二位同样,一直搜索到叶节点,那就是ans了,他的在记录下
index[ans], 那就是j了, 注意i有可能大于也有可能小于j,所以要
讨论一下i,j的大小
**/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<stack>
#include<vector>
typedef long long ll;
const int maxn = 1e6 + 304;
const int INF = 1e9;
using namespace std;
int T, kase = 1, n, now;
int ind[maxn], dig[50];
int newL, newans, newR;
int ansL, maxans, ansR;
int son[maxn][2], a[maxn];
void dfs(int g, int o, int i) {
if(i == 20) { newans = now ^ g; newR = ind[g]; return ; }
int t = !dig[i];
if(son[o][t]) {
dfs((g << 1) + t, son[o][t], i + 1);
} else {
dfs((g << 1) + dig[i], son[o][t ^ 1], i + 1);
}
}
void div(int k) {
for(int j = 19; j >= 0; j--) {
dig[j] = k & 1;
k >>= 1;
}
}
int main() {
scanf("%d", &T);
while(T--) {
memset(ind, 0, sizeof ind);
memset(son, 0, sizeof son);
int sum = 0; maxans = -1;
ansL = -1; ansR = -1; newans = 0;
ind[0] = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum ^= a[i];
if(sum && !ind[sum]) ind[sum] = i;
int o = 0;
div(sum);
for(int j = 0; j < 20; j++) {
if(dig[j] & 1) {
son[o][1] = o * 2 + 2;
o = son[o][1];
} else {
son[o][0] = o * 2 + 1;
o = son[o][0];
}
}
}
memset(dig, 0, sizeof dig);
int o = 0;
for(int j = 0; j < 20; j++) {
if(dig[j] & 1) {
son[o][1] = o * 2 + 2;
o = son[o][1];
} else {
son[o][0] = o * 2 + 1;
o = son[o][0];
}
}
now = 0;
for(int i = 1; i <= n; i++) {
now ^= a[i]; div(now);
newL = i;
dfs(0, 0, 0);
if(newL > newR) swap(newL, newR);
newL++;
if(newans > maxans) {
maxans = newans;
ansL = newL; ansR = newR;
} else if(newans == maxans) {
if(newL < ansL || (ansL == newL && newR < ansR)) {
ansL = newL; ansR = newR;
}
}
}
printf("Case #%d:\n", kase++);
printf("%d %d\n", ansL, ansR);
}
return 0;
}
华中农业大学第五届程序设计大赛网络同步赛 H MathematicalGame
最新推荐文章于 2018-05-20 12:11:20 发布