補題地址:https://nanti.jisuanke.com/t/A1607
題目:
Consider an array A with n elements . Each of its element is A[i](1≤i≤n). Then gives two integers Q, K, and Q queries follow . Each query , give you L, R, you can get Z by the following rules.
To get Z , at first you need to choose some elements from A[L] to A[R] ,we call them A[i1],A[i2]…A[it] , Then you can get number Z=K or (A[i1] xor A[i2] … xor A[it]) .
Please calculate the maximum ZZ for each query .
Input
Several test cases .
First line an integer T (1≤T≤10) . Indicates the number of test cases.Then T test cases follows . Each test case begins with three integer N, Q,K (1≤N≤10000, 1≤Q≤100000, 0≤K≤100000). The next line has N integers indicate A[1] to A[N] (0≤A[i]≤108). Then Q lines , each line two integer L,R (1≤L≤R≤N).
Output
For each query , print the answer in a single line.
樣例輸入
1 5 3 0 1 2 3 4 5 1 3 2 4 3 5
樣例輸出
3 7 7
解題思路:
求區間異或值or已知數k的最大值,區間問題,多次查詢,要用到線段樹,線段樹的葉子節點是單個的線性基,非葉子都是合併後的線性基。
線性基的合並可以參考這道例題。
本題很關鍵的一個點:因爲要求或之後的結果最大,k已知,那麼k中爲1的位或之後還是1,所以k中爲0的那些位需要用區間異或值來彌補,這就是說,k中爲0的那些位,在區間異或值中儘量爲1,且求最大異或值時只考慮區間的數m 在k中爲0的那些位。此時,我們不關心區間異或值是否取最大,或者取多少,只要最終的整個結果最大即可。
對k各位取反,在讓區間中的值都和取反後的k做與運算,這樣以後,原k中爲1的那些位 在區間中的數上 都是0 (因爲k中爲1的位取反後是0,0和任何數與都是0),而區間的數中爲1的位說明 原k中那些位爲0,原數中那些位爲1。所以最終答案就變成了求新區間的數的最大異或值(讓原k中爲0的那些位所表示的數最大),再與原k做或運算(原k中爲1的那些位在最大異或值中都是0),舉個例子試試吧。
ac代碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn=10005;
const int max_base=30;
typedef long long ll;
int a[maxn],xian[4*maxn][max_base+3],ans[max_base+3];
int n,q,k,t,L,R;
void update(int id)
{
int x=id<<1,y=id<<1|1;
memcpy(xian[id],xian[x],sizeof(xian[x]));//id的線性基初始化左子樹的線性基
for(int k=max_base;k>=0;k--)
{
int tmp=xian[y][k];
for(int j=max_base;j>=0 && tmp;j--)
{
if(tmp>>j&1)
{
if(xian[id][j])
tmp^=xian[id][j];
else
{
xian[id][j]=tmp;
break;
}
}
}
}
}
void build(int l,int r,int id)
{
if(l==r)
{
for(int i=max_base;i>=0;i--)
if(a[l]>>i&1)
{
if(xian[id][i])
a[l]^=xian[id][i];
else
{
xian[id][i]=a[l];
break;
}
}
return ;
}
int mid=(l+r)>>1;
build(l,mid,id<<1);
build(mid+1,r,id<<1|1);
update(id);
}
void query(int l,int r,int x,int y,int id)//要查的區間爲(x,y)
{
if(x<=l && r<=y)//如果區間不能直接查到的話,線性基合併,合併的結果存入ans【】中
{
for(int k=max_base;k>=0;k--)
{
int tmp=xian[id][k];
for(int j=max_base;j>=0 && tmp;j--)
{
if(tmp>>j&1)
{
if(ans[j])
tmp^=ans[j];
else
{
ans[j]=tmp;
break;
}
}
}
}
return ;//記得寫!
}
int mid=(l+r)>>1;
if(x<=mid) query(l,mid,x,y,id<<1);
if(y>=mid+1) query(mid+1,r,x,y,id<<1|1);
}
int main() {
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
scanf("%d",&t);
while(t--)
{
memset(xian,0,sizeof(xian));
scanf("%d %d %d",&n,&q,&k);
k=~k;
for(int i=1;i<=n;i++)
{
scanf("%d", &a[i]);
a[i]&=k;
}
k=~k;
build(1,n,1);
while(q--)
{
scanf("%d %d",&L,&R);
memset(ans,0,sizeof(ans));
query(1,n,L,R,1);
int res=0;
for(int i=max_base;i>=0;i--)
res=max(res,res^ans[i]);
printf("%d\n",res|k);
}
}
return 0;
}