【解题报告】CF1100F Ivan and Burgers 线性基无修改区间询问

线性基的区间查询,n个数,m次查询区间的最大异或和,每次查询有左边界L右边界R。
用线段树肯定可以维护,但据说被卡,咱也没试。
看了遍题解,大概有这么两种做法,两者的基本思路是差不多的,区间的右边界一定,看左边界能不能行,所以要有个新数组pos来记录对于每一位,对这位产生贡献的是第几个数,如果这个第几个数在比左边界大,说明在范围里,就算上,否则跳过。但是后边那个的空间开销更小:

第一种比较好理解,在线操作,但额外开销有点大:前缀线性基

参考:https://blog.csdn.net/u011815404/article/details/101163641

#include 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int n;
const int maxn=5e5+10;
//线性基
struct L_B{
    int d[maxn][32],pos[maxn][32];//d存线性基,d[i]代表1~i的前缀线性基;
    //pos[i][j]的含义是对于1~i的数,最后一个修改j位的数的id号是多少
    int cnt;//insert的时候记录这是第几个数
    L_B(){
        memset(d,0,sizeof(d));
        memset(pos,0,sizeof(pos));
        cnt=0;
    }
    void clear(){
        memset(d,0,sizeof(d));
        memset(pos,0,sizeof(pos));
        cnt=0;
    }
    bool insert(int val){
    	++cnt;//第cnt个数
    	int P=cnt;
		for(int i=0;i<32;i++){//复制上前缀
    		d[cnt][i]=d[cnt-1][i];
    		pos[cnt][i]=pos[cnt-1][i];
		}
        for(int i=31;i>=0;i--){
        	if((val&(1<0;          //大于0则是成功加入线性基的向量
    }
    int query_max(int left,int right){
        int ans=0;
        for(int i=31;i>=0;i--){
        	if(pos[right][i]>=left){//对于1~right的线性基,如果对i位造成影响的数的序号在左边界右边则说明这一位的影响在范围内,说明影响有效,进行异或操作,否则不管
        		ans=max(ans,ans^d[right][i]);
			}
		}
		return ans;
    }
}xxj;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
    	int c;scanf("%d",&c);
    	xxj.insert(c);
	}
	int q;scanf("%d",&q);
	while(q--){
		int left,right;
		scanf("%d%d",&left,&right);
		printf("%d\n",xxj.query_max(left,right));
	}
}

第二种:扫描r,维护线性基中每位的pos,离线操作

参考:https://blog.csdn.net/C20181220_xiang_m_y/article/details/106060335

#include
#define maxn 500005
using namespace std;
int n,m,a[maxn],d[20],pos[20],ans[maxn];
struct node{
	int l,r,id;
	bool operator < (const node &p)const{return r=0;i--) if(x&(1<pos[i]) swap(t,pos[i]),swap(x,d[i]); x^=d[i];}
		else {d[i]=x,pos[i]=t; return;}
	}
}
int qry(int t){//这也是 
	int ret=0;
	for(int i=19;i>=0;i--) 
		if(pos[i]>=t) 
			ret=max(ret,ret^d[i]);
	return ret;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
	sort(q+1,q+1+m);
	//按r排序,为什么呢?因为我们要一个个插入,插的时候插到哪把在已经插过的范围里的询问求出来 
	for(int i=1,j=1;i<=n;i++){
		insert(a[i],i);//插进去的数,和这个数是第几个数
		for(;j<=m&&q[j].r==i;j++) ans[q[j].id]=qry(q[j].l);
		//原来写的是r<=i,其实==更好理解点... 
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}


你可能感兴趣的:(个人学习感悟)