zoukankan      html  css  js  c++  java
  • 【BZOJ4184】shallot(线性基+线段树分治)

    点此看题面

    大致题意: 让你支持三种操作:加入一个数,删除一个数,求选出任意数的最大异或和。

    动态线性基

    首先看到选出任意数的最大异或和,肯定会想到线性基。

    然后再看看它要我们支持加入和删除,加入还好,删除是什么鬼东西?

    于是就需要请出我们的动态线性基了。

    实际上,根据常规套路,很多动态的问题都可以转化为离线搞,这题也不例外。

    线段树分治

    线段树分治是一个很有意思的算法。

    这题中,我们可以离线求出每个数的出现时间区间,然后对线段树上表示这一区间的节点们开个(vector)扔入这个数。

    询问时我们遍历线段树,对每一层开一个线性基,每个线性基从父节点那里继承过来,然后再往线性基中插入该节点上的数。

    容易发现每个叶节点上维护的正是这一时间存在的所有数。

    于是这道题就做完了,代码也是很简洁的。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define LN 25
    #define LV 30
    using namespace std;
    int n;map<int,int> p;
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int f,T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0,f=1;W(!D) f=c^'-'?1:-1;W(x=tn+(c&15),D);x*=f;}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class LinearBasis//线性基
    {
    	private:
    		int v[LV+5];
    	public:
    		I void Ins(RI x) {for(RI i=LV;~i;--i) if(x>>i&1) {if(!v[i]) return (void)(v[i]=x);x^=v[i];}}//插入
    		I int Qry(RI t=0) {for(RI i=LV;~i;--i) t<(t^v[i])&&(t^=v[i]);return t;}//询问
    };
    class SegmentTree//线段树
    {
    	private:
    		#define PT CI l=1,CI r=n,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		LinearBasis B[LN+5];vector<int> V[N<<2];vector<int>::iterator it;
    	public:
    		I void Add(CI x,CI L,CI R,PT)//往一个区间内扔入x
    		{
    			if(L<=l&&r<=R) return V[rt].push_back(x);int mid=l+r>>1;
    			L<=mid&&(Add(x,L,R,LT),0),R>mid&&(Add(x,L,R,RT),0);
    		}
    		I void Solve(CI d=0,PT)//线段树分治
    		{
    			for(it=V[rt].begin();it!=V[rt].end();++it) B[d].Ins(*it);//插入该节点上的数
    			if(l==r) return F.writeln(B[d].Qry());int mid=l+r>>1;//边界直接询问输出
    			B[d+1]=B[d],Solve(d+1,LT),B[d+1]=B[d],Solve(d+1,RT);//把线性基继承给儿子
    		}
    }S;
    int main()
    {
    	RI i,x;for(F.read(n),i=1;i<=n;++i) F.read(x),x>0?p[x]=i:(x=-x,S.Add(x,p[x],i-1),p.erase(x),0);//开map记录一个数上次出现位置
    	for(map<int,int>::iterator it=p.begin();it!=p.end();++it) S.Add(it->first,it->second,n);//把没被删掉过的数扔入线段树
    	return S.Solve(),F.clear(),0;
    }
    
  • 相关阅读:
    contextMenu,右键菜单
    hashchange
    web攻击日志分析之新手指南
    工匠人生
    数学有卵用之通信篇
    精英主义(一)
    flaskbb部署笔记
    深入分析一波,你们说的云安全到底是什么鬼?
    Gh0st与云安全
    困境与突破
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4184.html
Copyright © 2011-2022 走看看