题目:
样例输入:
2
5
10 10 10 10 10
4
1 1 2 1
样例输出:
10
3
题意:多组样例,每组样例给出一个n,代表数组中元素的个数,接下来一行给出n个数,我们可以对其进行操作,每次操作选定一个区间[l,r],操作后将位于[l,r]的数全部变成a[l]^a[l+1]^……^a[r],我们最后要把整个数组变成一样的值,问我们能够得到的最大的值是多少?一开始的n个数保证有两个数是相同的。
分析:这道题的结论是从这n个数里面选出来任意多个数并进行异或,能够得到的最大值就是答案。
证明:假如我们有n个数a[1~n],其中a[p1],a[p2],……,a[pk]k个数的异或值是最大的,那么我们就要证明我们最终的答案可以是a[p1]^a[p2]^……^a[pk],假设p1<p2<……<pk
先来考虑把区间[p1,p2]里面的数全部变为a[p1]^p[p2]
假如p2=p1+1,那么两者可以直接异或
假如p2>p1+2,也就是说p1和p2之间至少有两个数,那么我们可以通过操作区间[p1+1,p2-1]然其全部变为0,比如假如该区间有偶数个数,那么我们可以两两相邻进行异或,然后每两个相邻的数都变为了相同的数,再次进行相同操作,那么该区间的所有数都会变为0,最后使得区间[p1,p2]内的数全部异或即可完成目的。假如两者中间有奇数个数,以三个数为例,x,y,z,先让x,y异或两次全部变为0,然后再让与z相邻的那个0和z异或两次,就可以将三者都变为0,那么操作完后也可以将区间中的所有数全部变为0.
最后一种情况就是p1和p2之间只有一个数的情况,这就是为什么题目中一定有两个数要相同了。这种情况下我们不能先对p和p之间的数进行合并,假如两个相同的数是x,那么我们就先合并离x比较近的两个pi和pi+1,不妨就假设是在p1左边吧。极端情况就是(o代表任意数)
x,p1,o,p2,o,x这种情况
我们先将x和p1合异或一次得到y,y,o,p2,o,x(y=x^p1)
再将第二个位置和第三个位置连续异或两次全变为0得到:y,0,0,p2,o,x(y=x^p1)
再将前四个位置异或一次得到:z,z,z,z,o,x(z=x^p1^p2)
然后将第四个位置和第五个位置连续异或两次变为0得到:z,z,z,0,0,x
然后将后四个位置异或一次得到z,z,p1^p2,p1^p2,p1^p2,p1^p2
最后将前两个位置异或为0并将前三个进行异或一次得到p1^p2,p1^p2,p1^p2,p1^p2,p1^p2,p1^p2
通过上面介绍的这种方法就可以将整个数组得到任意想要异或得到的值了
剩下的就是求一个异或最大值了,这就是线性基的操作了,直接套上模板即可
线性基就是用来求异或最大值,最小值以及第k大值的
下面是代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+10;
struct L_B
{
const static int BASE = 63;
long long d[BASE], p[BASE];
int cnt, flag;
void init()
{
memset(d, 0, sizeof(d));
memset(p, 0, sizeof(p));
cnt = 0;
flag = 0;
} // 1e18以内的数都适用.
inline bool insert(long long val)
{
for (int i=BASE- 1; i >= 0; i--)
{
if (val & (1ll << i))
{
if (!d[i])
{
d[i] = val;
return true;
}
val ^= d[i];
}
}
flag = 1; //可以异或出0
return false;
// 可判断val是否存在于线性基当中.
}
long long query_max()
{
long long res = 0;
for (int i = BASE - 1; i >= 0; i--)
{
if ((res ^ d[i]) > res)
res ^= d[i];
}
return res;
}
long long query_min()
{ // 应该预先判断能否是0的情况..QAQ
if (flag)
return 0;
for (int i = 0; i <= BASE - 1; i++)
{
if (d[i])
return d[i];
}
}
void rebuild()
{ // 用于求第k小值.需要先进行独立预处理
for (int i = BASE - 1; i >= 0; i--)
{
for (int j = i - 1; j >= 0; j--)
{
if (d[i] & (1ll << j))
d[i] ^= d[j];
}
}
for (int i = 0; i <= BASE - 1; i++)
{
if (d[i])
p[cnt++] = d[i];
}
}
long long kthquery(long long k)
{ // 注意判断原序列异或出0的情况, 此时应该将k -- 在进行后面的操作.
if (flag) //判0
--k;
if (!k)
return 0;
long long res = 0;
if (k >= (1ll << cnt))
return -1;
for (int i = BASE - 1; i >= 0; i--)
{
if (k & (1LL << i))
res ^= p[i];
}
return res;
}
void Merge(const L_B &b)
{ // 把b这个线性基插入到当前这个线性基中.
for (int i = BASE - 1; i >= 0; i--)
if (b.d[i])
insert(b.d[i]);
}
} LB;
int main()
{
int T;
cin>>T;
while(T--)
{
L_B p;
p.init();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
long long t;
scanf("%lld",&t);
p.insert(t);
}
printf("%lld\n",p.query_max());
}
return 0;
}