bzoj4184shallot
题意
给出一个初始为空的数字集合,每次添加一个数字/删除一个存在的数字,然后输出选出一些数进行异或能够得到的最大数值.操作次数<=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的更多相关文章
随机推荐
- 20145234黄斐《Java程序设计》实验一—Java开发环境的熟悉(Linux + Eclipse)
实验步骤 由于实验时间比较紧张,这里只有最终结果的截图 (一)命令行下Java程序开发 (二)Eclipse下Java程序开发.调试 (三)练习 实现求正整数1-N之间所有质数的功能,并进行测试 实验 ...
- EXCEL 处理重复数据名字后面追加值
近期要用 EXCEL 处理重复数据名字后面追加值的,如图: 先排序,再根据条件追加 [公式]=+B6&IF(COUNTIF($B$6:B6,B6)-1>0,"_" & ...
- mysql导入报错【The MySQL server is running with the --event-scheduler=DISABLED】
一.问题: 在进行mysql操作导入库的时候,报出了[The MySQL server is running with the --event-scheduler=DISABLED] 查看后台日志是事 ...
- day1 函数 (独立功能代码块)
1.引入函数 2.函数执行过程 4.带参数的函数 5.带返回值的函数 6. 多个返回值 (return a,b,c)元组 7.4种函数 1.引入函数 独立功能代码块 ---> 封装 ----&g ...
- [CF888E] Maximum Subsequence 序列分治
早期作品,不喜轻喷. LG传送门 序列分治板子题. 切这道题用了好长时间,所以想发篇题解作为纪念 . 首先,我们认真观察题目数据(面向数据做题是个好习惯),发现题目的\(n\)竟然只有\(35\),我 ...
- jquery.validate使用 - 5
一些常用的验证脚本 不会写js了,只能从网上找一些常用的验证脚本. // 手机号码验证jQuery.validator.addMethod("mobile", function(v ...
- 面向忙碌开发者的 Android
面向忙碌开发者的 Android passiontim 关注 2016.11.19 21:41* 字数 4013 阅读 2967评论 2喜欢 92 面向忙碌开发者的 Android 视频教程(Tuts ...
- 视觉SLAM中的深度估计问题
一.研究背景 视觉SLAM需要获取世界坐标系中点的深度. 世界坐标系到像素坐标系的转换为(深度即Z): 深度的获取一共分两种方式: a)主动式 RGB-D相机按照原理又分为结构光测距.ToF相机 To ...
- 使用Xamarin实现串口通讯
前几天我写了年度总结,然后有人说让我教一下他Xamarin串口通讯怎么做,其实跟java没有多大区别. 记得我刚开始接到公司这个项目的时候很懵逼,我去看了别的安卓串口工具,都不行我当时是RS232串口 ...
- linux 下 mysql安装和配置
最近在学习R语言,看到R与数据库交互这一部分,就自己动手实践了一下,数据库选择的是mysql,主要记录下linux下怎么安装mysql. 网上的很多资料都有相关的文章,这里只是记录下自己安装过程中遇到 ...