HDU6579——Operation 線性基 貪心

HDU6579——Operation

題意

給你一個長度爲nn的數列,有mm次的操作,操作分爲兩種:

  1. 查詢區間[l,r][l,r]中一些數字的異或最大值。
  2. 在數組的尾部插入一個數字xx

數據範圍

T10T \le 10
1n5×1051\le n \le 5\times10^5
1m5×1051\le m\le 5\times 10^5
0ai2300\le a_i\le 2^{30}
n106m1060x230\sum n \le 10^6\quad\sum m\le 10^6\quad 0\le x\le 2^{30}

題解

由於線性基的長度很短,不超過30,所以我們可以將數組的前綴線性基保留下來([1,i][1,i])。而區間[1,i][1,i]的線性基可以由[1,i1][1,i-1]通過添加a[i]a[i]來獲得。
如果我們已經得到了線性基,那麼久可以查詢前綴區間的子集異或最大值。但是關鍵點是如果在查詢[L,R][L,R]的時候,避免[1,L1][1,L-1]的線性基的干擾(去除在[L,R][L,R]區間中不需要的線性基)。
首先,我們考慮現線性基的插入問題,如果線性基在當前的位置上已經有值了,我們就不能把待插入的值放在這個位置。那麼我們爲了方便處理,我們將每個位置基礎的值爲對應數列的最右側的數字。
具體的操作如下:
我們額外的保存線性基的每一個位置的數在原來數列中的位置。插入的時候,如果對應的位置更靠左,那麼就將待插入的數字和它交換。如果沒有數字,那麼就佔據這個位置。
基於這個策略,我們在查詢區間[L,R][L,R]的時候,可以在區間[1,R][1,R]對應的線性基中去尋找,如果線性基上的位置比LL要大,那麼就考慮對答案的貢獻,否則就直接跳過這一位。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 1e6+10;

int arr[maxn], bc[maxn], pos[maxn], v[maxn][32], p[maxn][32];


void add(int x, int value) {
    int t = x ;
    for(int  i = 30;i>=0;i--) {
        if(value &(1<<i)){
            if(!bc[i]) {
                bc[i] = value;
                pos[i] = x;
                break;
            }
            else if (pos[i] < x){
                swap(bc[i], value);
                swap(pos[i], x);
            }
            value ^= bc[i];
        }

    }
    for(int  i = 0;i<=30;i++) {
        v[t][i] = bc[i];
        p[t][i] = pos[i];
    }

}

int ask(int l ,int r) {
    int ans = 0 ;
    for(int i = 30;i>=0;i--) {
        if(p[r][i] >= l && (ans ^ v[r][i]) > ans) {
            ans ^= v[r][i];
        }
    }
    return ans;
}

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        memset(bc, 0, sizeof(bc));
        memset(pos, 0, sizeof(pos));
        int n, m ,op ;
        scanf("%d %d", &n, &m);
        for(int i = 1;i<=n;i++) {
            scanf("%d",&arr[i]);
            add(i, arr[i]);
        }

        int last = 0, l, r,value;
        while (m--) {
            scanf("%d", &op);
            if(op) {
                scanf("%d",&value);
                value ^= last ;
                add(++n, value);
            } else {
                scanf("%d %d", &l,&r);
                l = (l^last)%n +1;
                r = (r^last) %n + 1;
                if(l > r) {
                    swap(l,r);
                }
                last = ask(l,r);
                printf("%d\n", last);
            }
        }
    }
    return 0;
}

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