线性基 + 并查集

给一个1-base数组{a},有N次操作,每次操作会使一个位置无效。一个区间的权值定义为这个区间里选出一些数的异或和的最大值。求在每次操作前,所有不包含无效位置的区间的权值的最大值。

输入描述:

第一行读入一个正整数(1 <= n <= 105)

第二行读入n个正整数,第i个表示a[i](0<= a[i] <= 109)

第三行读入n个正整数,第i个表示x[i]即第i次操作的位置,保证x[i]互不相同。

输出描述:

输出n行答案
示例1

输入

10
169 816 709 896 58 490 97 254 99 796 
4 2 3 10 5 6 1 8 9 7

输出

1023
994
994
994
490
490
254
254
99
97

题意 : 每次让一个位置得数失效,求解连续区间上异或值最大得是多少?
思路分析 : 每次让一个位置失效,那么这个问题就可以逆向去考虑,每次加一个数,这个总比删除操作好处理,用并查集就可以,然后就是一个在套一个线性基就可以
代码示例 :
#define ll long long
const int maxn = 1e5+5;
const int mod = 1e9+7;
const double eps = 1e-9;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;

int n;
int pre[maxn], pos[maxn];
int f[maxn];
int val[maxn][40];
int an[maxn];

int fid(int x){
    if (x != f[x]) f[x] = fid(f[x]);
    return f[x];
}

void insert(int b[], int x){
    for(int i = 30; i >= 0; i--){
        if (x>>i&1){
            if (!b[i]) {b[i] = x; break;}
            else x ^= b[i];
        }
    }
}

void unit(int p1, int p2){
    int x = fid(p1), y = fid(p2);
    for(int i = 0; i <= 30; i++){
        if (val[y][i]) insert(val[x], val[y][i]);
    }
    f[y] = x;
}

int solve(int p){
    int ans = 0;
    for(int i = 30; i >= 0; i--){
        if ((ans^val[p][i]) > ans) {
            ans ^= val[p][i];
        }
    }
    return ans;
}


int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    cin >> n;
    
    for(int i = 1; i <= n; i++) scanf("%d", &pre[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &pos[i]);
    memset(f, 0, sizeof(f));
    int ans = 0;
    for(int i = n; i >= 1; i--){
        int x = pos[i]; // 位置
        f[x] = x;
        insert(val[x], pre[x]);
        
        if (f[x-1]) unit(x, x-1);
        if (f[x+1]) unit(x, x+1);
        ans = max(ans, solve(x));
        an[i] = ans;
    }    
    for(int i = 1; i <= n; i++) printf("%d\n", an[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ccut-ry/p/8964278.html
今日推荐