题意

给出一个初始为空的数字集合,每次添加一个数字/删除一个存在的数字,然后输出选出一些数进行异或能够得到的最大数值.操作次数<=500000,数字大小<2^31

分析

看上去我们只要写一个支持插入删除的线性基就可以了,然而线性基不支持删除,因此我们需要把删除操作也转化为插入操作.解决的方法是在时间轴上考虑问题.

我们需要求解的是在时刻1,时刻2,时刻3....时刻n时的线性基,那么就相当于要知道这些时刻的数字集合.如果某个数字在时刻s被插入,时刻t之后被删除,那么相当于时刻s到时刻t的数字集合中都加入了这个数,也就是区间[s,t]中的每个集合都加入了这个数字.每个数字在哪些区间中加入可以在读入的时候用个map求出来.

于是问题转化为:有n个数字集合顺序排放,进行多次"往某一段区间[l,r]中每个集合都加入一个数x"的操作,最后查询每个数字集合的线性基.

可能是因为我比较蠢所以感觉这题的标算非常的妙

一开始,我没有利用最后查询的条件,想着直接把懒标记线段树套个线性基,每个线段树节点上挂着一个大小为32的数组存储所有能覆盖这段区间的数字的线性基,最后把所有标记暴力下传到叶节点,修改的时候动态打标记.这样做修改的总复杂度是O(nlogn*32),但是最后暴力下传标记的复杂度可以达到O(nlogn*32*32),绝对会T.而且内存也是不够用的.

然后看题解,发现标算同样是在时间轴上考虑问题,但是打标记的方法很高明.虽然对于求线性基的问题,我们有时需要在线段树一个节点上存储一些数字的线性基,这样会优化时间复杂度(例如SCOI2016幸运数字),但是有的时候,把线性基求出来是不必要的,在时间效率上是得不偿失的.在这道题中,我们只在所有修改都完成后才真正需要查询线性基.在进行每一次修改的时候都维护线性基是不必要的,并且存储了大量的冗余信息,把"l到r添加一个数字x"的操作拆成了一堆碎片,散布于某些节点的线性基中.造成的结果,就是最后不得不用O(nlogn*32*32)的复杂度暴力下传标记.

如果我们在线段树的标记里存储的信息不是线性基,而是"在这个节点中插入哪些数字",最后的答案统计的时间复杂度就变得清真起来.

具体做法是,我们在线段树每个节点开一个vector,对于区间加入一个数字的操作,我们往区间覆盖的每个线段树节点的vector里把这个数字加进去,每个数字加到最多O(logn)个节点中所以这样的空间复杂度是nlogn的.显然,最后某个位置的答案就是它对应的叶节点到线段树根节点路径上所有的vector里的数字的线性基.那么自然我们可以从线段树的根节点出发,求出根节点到每个节点路径上的数字的线性基BASE[x],某个节点的BASE[]可以由它的父节点的BASE[]中插入这个节点vector的数字得到.如果直接每个节点开大小为32的数组,还是会炸.其实我们可以dfs,保存当前的一条链即可.细节见代码.

#include<cstdio>
#include<vector>
#include<map>
using namespace std;
const int maxn=500005;
struct Base{
int b[31];
int val(){
int ans=0;
for(int i=0;i<31;++i)ans^=b[i];
return ans;
}
void insert(int x){
for(int i=30;i>=0;--i){
if(x>>i&1){
if(b[i])x^=b[i];
else{
b[i]=x;
for(int k=i-1;k>=0;--k)if(b[i]>>k&1)b[i]^=b[k];
for(int k=i+1;k<31;++k)if(b[k]>>i&1)b[k]^=b[i];
break;
}
}
}
}
}B[32];
vector<int> a[maxn<<2];
void add(int rt,int l,int r,int ql,int qr,int x){//if(rt==1)printf("%d %d %d\n",ql,qr,x);
if(ql<=l&&r<=qr){
a[rt].push_back(x);return;
}
int mid=(l+r)>>1;
if(ql<=mid)add(rt<<1,l,mid,ql,qr,x);
if(qr>mid) add(rt<<1|1,mid+1,r,ql,qr,x);
}
void query(int rt,int l,int r,int dep){
for(vector<int>::iterator pt=a[rt].begin();pt!=a[rt].end();++pt){
B[dep].insert(*pt);
}
if(l==r)printf("%d\n",B[dep].val());
else{
int mid=(l+r)>>1;
B[dep+1]=B[dep];
query(rt<<1,l,mid,dep+1);
B[dep+1]=B[dep];
query(rt<<1|1,mid+1,r,dep+1);
}
}
int main(){
int n;scanf("%d",&n);
int x;
map<int,int> dict;
map<int,int> last;
for(int i=1;i<=n;++i){
scanf("%d",&x);
if(x<0){
dict[-x]--;
if(dict[-x]==0){
add(1,1,n,last[-x],i-1,-x);
}
}else{
dict[x]++;
if(dict[x]==1)last[x]=i;
}
}
for(map<int,int>::iterator pt=dict.begin();pt!=dict.end();++pt){
if(pt->second!=0){
add(1,1,n,last[pt->first],n,pt->first);
}
}
query(1,1,n,0);
return 0;
}

bzoj4184shallot的更多相关文章

随机推荐

  1. 20155334 实验四:Android程序设计

    20155334实验四:Android程序设计 实验内容 基于Android Studio开发简单的Android应用并部署测试; 了解Android组件.布局管理器的使用: 掌握Android中事件 ...

  2. 分享daocloud联合创始人陈齐彦关于docker的一段阐述

    罗比,本名陈齐彦,他在加入DaoCloud之前是EMC中国研究院的总架构师,云平台及应用实验室的创始人.谈及创业的初心,他激动了起来: 容器这东西和当年Hadoop一样,是互联网技术对企业IT技术的逆 ...

  3. 【LG5017】[NOIP2018pj]摆渡车

    [LG5017][NOIP2018pj]摆渡车 题面 洛谷 题解 震惊!普及组竟然考斜率优化??? 当然有其他的方法 首先我们转化一下模型 此题可以变为: 在一根时间轴上有一些点,每个时间点\(i\) ...

  4. Linux 的字符串截取

    假设有变量 var=http://www.aaa.com/123.htm 1. # 号截取,删除左边字符,保留右边字符. echo ${var#*//} 其中 var 是变量名,# 号是运算符,*// ...

  5. 常用SQL语句大全(SQL Server)

    一.基础       查看数据库状态 select state_desc from sys.databases where name='dbname'  -- dbname数据库名 1.说明:创建数据 ...

  6. MAC终端安装指定版本node

    MAC终端安装指定版本node 安装brew 终端上运行 $ /usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Home ...

  7. 测试类异常Manual close is not allowed over a Spring managed SqlSession

    在用Spring 和mybatis整合的 写测试类的时候报出解决办法:在全局配置文件   class="org.mybatis.spring.SqlSessionTemplate" ...

  8. 7个Node.js的Web框架

    NodeJS也就是Node,是众所周知的使用javascript构建Web应用框架,它启动一个服务器非常简单,如下: var http = require('http'); http.createSe ...

  9. Mysql 单表主从同步

    先配主从同步,后将主库表老数据传输到从库 说明:api-server的数据库为主,其他harbor为从 1.master 配置文件更改 [mysqld] log-bin = mysql-bin ser ...

  10. IncDec序列:差分+贪心

    IncDec序列 题目描述: 给定一个长度为 n 的数列 a1,a2,…,an,每次可以选择一个区间[l,r],使下标在这个区间内的数都加一或者都减一. 求至少需要多少次操作才能使数列中的所有数都一样 ...