【2017西安邀請賽:A】XOR(區間異或最大值多次查詢---線段樹+線性基合併)

補題地址: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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章