• 【BZOJ4184】shallot 线段树+vector+线性基


    【BZOJ4184】shallot

    Description

    小苗去市场上买了一捆小葱苗,她突然一时兴起,于是她在每颗小葱苗上写上一个数字,然后把小葱叫过来玩游戏。

    每个时刻她会给小葱一颗小葱苗或者是从小葱手里拿走一颗小葱苗,并且
    让小葱从自己手中的小葱苗里选出一些小葱苗使得选出的小葱苗上的数字的异或和最大。
    这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为Oi选手的你,你能帮帮他吗?
    你只需要输出最大的异或和即可,若小葱手中没有小葱苗则输出0。

    Input

    第一行一个正整数n表示总时间;第二行n个整数a1,a2...an,若ai大于0代表给了小葱一颗数字为ai的小葱苗,否则代表从小葱手中拿走一颗数字为-ai的小葱苗。

    Output

    输出共n行,每行一个整数代表第i个时刻的最大异或和。

    Sample Input

    6
    1 2 3 4 -2 -3

    Sample Output

    1
    3
    3
    7
    7
    5

    HINT

     N<=500000,Ai<=2^31-1

    题解:因为线性基不支持删除操作,所以我们要考虑离线的做法

    有一个性质很重要:每个数都存在于一段连续的区间,所以我们可以用map来记录区间的起始位置和结束位置,然后用线段树来实现区间操作。

    具体方法是给线段树上的每一个节点都开一个vector,vector里维护的就是线性基,每次更新到一整块区间就在线性基中加入这个数,并维护线性基。查询的时候我们将每个点到根的路径上的所有的线性基再开一个vector扔进去,并维护线性基,然后贪心求出最大值就行了。

    一开始感觉空间复杂度有点吓人,不过当我TLE时才发现其实内存完全不虚。

    然后发现,算法的瓶颈其实在于查询操作,所以我们不能每次都进行单点查询,而是遍历整棵线段树,并输出所有的答案。

    拍极限数据的时候我跑了6、7秒感觉GG,但是测了一下标程才发现标程比我的还慢,所以果断AC。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #include <map>
    #define lson x<<1
    #define rson x<<1|1
    using namespace std;
    const int maxn=500010;
    int n,m;
    int last[maxn],A[maxn];
    map<int,int> mp;
    struct line
    {
    	vector<int> v;
    	int gauss(int x)
    	{
    		int i;
    		for(i=0;i<v.size();i++)	if((x^v[i])<x)	x^=v[i];
    		if(x)
    		{
    			v.push_back(x);
    			for(i=v.size()-1;i;i--)	if(v[i]>v[i-1])	swap(v[i],v[i-1]);
    		}
    		return x;
    	}
    	int getmax()
    	{
    		int i,ret=0;
    		for(i=0;i<v.size();i++)	if((ret^v[i])>ret)	ret^=v[i];
    		return ret;
    	}
    };
    line s[maxn<<2],emp;
    void updata(int l,int r,int x,int a,int b,int c)
    {
    	if(a<=l&&r<=b)
    	{
    		s[x].gauss(c);
    		return ;
    	}
    	int mid=l+r>>1;
    	if(a<=mid)	updata(l,mid,lson,a,b,c);
    	if(b>mid)	updata(mid+1,r,rson,a,b,c);
    }
    void query(int l,int r,int x,line q)
    {
    	for(int i=0;i<s[x].v.size();i++)	q.gauss(s[x].v[i]);
    	if(l==r)
    	{
    		printf("%d
    ",q.getmax());
    		return ;
    	}
    	int mid=l+r>>1;
    	query(l,mid,lson,q),query(mid+1,r,rson,q);
    }
    int main()
    {
    	scanf("%d",&m);
    	int i,a;
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d",&A[i]);
    		if(A[i]<0)	a=mp[-A[i]],last[a]=i-1;
    		else	mp[A[i]]=i;
    	}
    	for(i=1;i<=m;i++)
    	{
    		if(A[i]<0)	continue;
    		if(!last[i])	last[i]=m;
    		updata(1,m,1,i,last[i],A[i]);
    	}
    	query(1,m,1,emp);
    	return 0;
    }
  • 相关阅读:
    ubuntu重新安装mysql
    linux基本命令
    ubuntu启用root用户
    cada的常规使用
    如果有人对我的mysql的笔记感兴趣请联系我,互相学习
    10、mysql查看进程
    09、Mysql 查询是否锁表
    08、查看锁记录等待时间:
    针对发送网络附件的java方法(使用Apache的jar包调用)
    mysql的卸载重装+导入大量数据失败的解决方案+工具执行和项目执行结果不同
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7054379.html
Copyright © 2020-2023  润新知