P4839 P哥的桶 题解(线段树维护线性基)
题目链接
题目大意
有\(n\)个桶,\(m\)次操作
操作分为两种
-
在\(pos\)桶中加入一个\(val\)值
-
求\([l,r]\)中选任意个桶使得异或和最大,求最大的异或和
注意每个节点是一个桶可以放多个值
\(n,m\leq 5\times 10^4\)
题目思路
单点修改,区间查询,异或最大值
很显然是线段树维护线性基
然后这样的复杂度是\(O(n\times log^3)\)
然而会\(TLE\)
看了一下大佬的题解,更新的时候没有必要维护两个子节点的并集
只需要从上往下更新,每到一个节点就更新其线性基维护的值就好了。
查询的时候每次都将不同区间所维护的线性基合并到答案即可
插入复杂度\(O(n\times log^2)\)
查询复杂度\(O(n\times log^3)\)
但是这个查询的常数还是大大优化了
TLE代码
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
typedef pair<double,int> pdi;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=5e4+5,mod=1e7;
int n,m;
int a[maxn];
int p[40];
vector<int> tree[maxn<<2];
void ins(int x) {
for(int i = 30;i>=0; i--) {
if (!(x &(1<<i))) continue;
// x的第i位是0
if(!p[i]){
p[i] = x;
break;
}
x^= p[i];
}
}
vector<int> mer(vector<int> a,vector<int> b){
vector<int> ans;
memset(p,0,sizeof(p));
for(int i=0;i<a.size();i++) ins(a[i]);
for(int i=0;i<b.size();i++) ins(b[i]);
for(int i=0;i<=30;i++){
if(p[i]!=0){
ans.push_back(p[i]);
}
}
return ans;
}
void update(int node,int pos,int l,int r,int val){
if(l==r){
vector<int> temp;
temp.push_back(val);
tree[node]=mer(tree[node],temp);
return ;
}
int mid=(l+r)/2;
if(mid>=pos) update(node<<1,pos,l,mid,val);
else update(node<<1|1,pos,mid+1,r,val);
tree[node]=mer(tree[node<<1],tree[node<<1|1]);
}
vector<int> query(int node,int L,int R,int l,int r){
if(L<=l&&r<=R){
return tree[node];
}
vector<int> ans;
int mid=(l+r)/2;
if(mid>=L) ans=mer(ans,query(node<<1,L,R,l,mid));
if(mid<R) ans=mer(ans,query(node<<1|1,L,R,mid+1,r));
return ans;
}
signed main(){
scanf("%d%d",&m,&n);
for(int i=1,opt,pos,val,l,r;i<=m;i++){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&pos,&val);
update(1,pos,1,n,val);
}else{
scanf("%d%d",&l,&r);
vector<int> ans=query(1,l,r,1,n);
int pr=0;
for(int j=(int)ans.size()-1;j>=0;j--){
pr=max(pr,pr^ans[j]);
}
printf("%d\n",pr);
}
}
return 0;
}
AC代码
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
typedef pair<double,int> pdi;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=5e4+5,mod=1e7;
int n,m;
int a[maxn];
int tree[maxn<<2][40];
void ins(int pos,int x) {
for(int i = 30;i>=0; i--) {
if (!(x &(1<<i))) continue;
// x的第i位是0
if(!tree[pos][i]){
tree[pos][i] = x;
break;
}
x^= tree[pos][i];
}
}
void update(int node,int pos,int l,int r,int val){
ins(node,val);
if(l==r){
return ;
}
int mid=(l+r)/2;
if(mid>=pos) update(node<<1,pos,l,mid,val);
else update(node<<1|1,pos,mid+1,r,val);
}
void query(int node,int L,int R,int l,int r){
if(L<=l&&r<=R){
for(int i=0;i<=30;i++){
if(!tree[node][i]) continue;
ins(0,tree[node][i]);
}
return ;
}
int mid=(l+r)/2;
if(mid>=L) query(node<<1,L,R,l,mid);
if(mid<R) query(node<<1|1,L,R,mid+1,r);
}
signed main(){
scanf("%d%d",&m,&n);
for(int i=1,opt,pos,val,l,r;i<=m;i++){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&pos,&val);
update(1,pos,1,n,val);
}else{
scanf("%d%d",&l,&r);
memset(tree[0],0,sizeof(tree[0]));
query(1,l,r,1,n);
int pr=0;
for(int i=30;i>=0;i--){ // tree[0]为答案
pr=max(pr,pr^tree[0][i]);
}
printf("%d\n",pr);
}
}
return 0;
}
不摆烂了,写题