链接:P哥的桶
题意:
给你 m 个位置和 n 次操作,操作有两种类型 :
操作一 : 表示在区间[l , r]中选择任意个位置,使这些位置中的数的异或和最大。
操作二 : 在位置 k 加入一个数x(每个位置可以有多个数)。
思路:
单点修改 + 区间查询首先考虑线段树 , 异或和最大考虑线性基。我们可以用线段树的每个节点表示这个区间的线性基,在修改的时候把每个包含这个位置的区间都插入一遍这个数就好了。然后查询可以把小区间的线性基合并到大区间,再根据这个大区间的线性基求解。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6 + 7;
const int mod = 998244353;
int n,m;
ll sum[maxn][33];
ll ans[33];
void update(int pos,int val,int l,int r,int rt){
int v = val;
for(int i = 31; i >= 0; i --){
if((v >> i) & 1){
if(sum[rt][i]){
v ^= sum[rt][i];
}
else{
sum[rt][i] = v;
break;
}
}
}
if(l == r){
return ;
}
int mid = (l + r) / 2;
if(pos <= mid) update(pos , val , l , mid , rt << 1);
if(pos > mid) update(pos , val , mid + 1 , r , rt << 1 | 1);
}
void query(int L,int R,int l,int r,int rt){
if(L <= l && R >= r){
for(int now = 31; now >= 0; now --){
int v = sum[rt][now] ;
for(int i = 31; i >= 0; i --){
if((v >> i) & 1){
if(ans[i]){
v ^= ans[i];
}
else{
ans[i] = v;
break;
}
}
}
}
return ;
}
int mid = (l + r) / 2;
if(L <= mid) query(L , R , l , mid , rt << 1);
if(R > mid) query(L, R, mid + 1,r,rt << 1 | 1);
}
int main(){
scanf("%d%d",&n,&m);
int op,l,r;
for(int i = 1; i <= n; i ++){
scanf("%d%d%d",&op,&l,&r);
if(op == 1){
update( l , r , 1 , m , 1);
}
else{
memset(ans,0,sizeof(ans));
query(l , r , 1 , m , 1);
ll val = 0;
for(int i = 31; i >= 0; i --){
if((val ^ ans[i]) > val) val ^= ans[i];
}
printf ("%lld\n",val);
}
}
}