B - Operation线性基入门:【条件动态维护区间线性基】【前缀线性基】

4 篇文章 0 订阅
4 篇文章 0 订阅

题目链接VJ
题目链接HDU
(本文是对%%%%%的学习笔记,同时使用了本题作为例题学。)
线性基真是个简单而强大的东西,然而我因为很少遇到线性基的题目基本没有怎么学习,线性基是一个基于贪心的数据处理技巧。
如果定义原序列通过若干个元素异或的到的新集合为序列的异或域,那么线性基就是一个异或域与原序列异或域相同的极小集合。

线性基三大性质:

1.原序列里面的任意一个数都可以由线性基里面的一些数异或得到
2.线性基里面的任意一些数异或起来都不能得到 0 00
3.线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的

首先是线性基能够维护的东西:

序列异或
(序列取若干个数异或起来的最值,即异或域的极值 :)
最小值:线性基最小值。
最大值:从高位到低位贪心取,以代码来说为:

if((res^d[i])>res)res^=d[i];

接下来就是线性基进阶技巧:

序列异或第k小
时间复杂度在于预处理 ( l o g A i ) 2 (log{A_i})^2 (logAi)2;
预处理就是让线性基的数位性质变化成为:当且仅当当前位取了,当前位的二进制位上才会为1;

void work()//处理线性基
{
	for(int i=1;i<=60;i++)
	for(int j=1;j<=i;j++)
	if(d[i]&(1ll<<(j-1)))d[i]^=d[j-1];
}
ll k_th(ll k)
{
	if(k==1&&tot<n)return 0;//特判一下,假如k=1,并且原来的序列可以异或出0,就要返回0,tot表示线性基中的元素个数,n表示序列长度
	if(tot<n)k--;//类似上面,去掉0的情况,因为线性基中只能异或出不为0的解
	work();
	ll ans=0;
	for(int i=0;i<=60;i++)
	if(d[i]!=0)
	{
		if(k%2==1)ans^=d[i];
		k/=2;
	}
}

当序列经过了处理,那么很显然相当于:
d 0 d 1 d 2 d 3 … … d t d_0d_1d_2d_3……d_t d0d1d2d3dt
其中 d i d_i di表示第i位的线性基取/不取。排名k自然也变成了d序列;
如果k = 10 1 ( 2 ) 101_(2) 101(2)
即:
第一小的线性基元素取了即: d 0 = 1 d_0=1 d0=1
第二小的线性基元素不取即: d 1 = 0 d_1=0 d1=0
第三小的线性基元素取了即: d 2 = 1 d_2=1 d2=1
于是第i位线性基元素贡献为: ( 1 < < i & k ) ∗ d [ i ] (1<<i\&k)*d[i] (1<<i&k)d[i];

动态维护序列线性基(带插入和删除)
因为过于少见,暂且备板,挂起来。


回到这个题:
本题希望维护区间线性基;修改操作只有在最右边放置新的数(仅有两种操作,一种是a[++n] = x,一种是query(l,r))

采用一个技巧:
线性基的性质之一就是只要保持线性基大小不变,数具体是哪些是可以变的。于是使用前缀线性基:
查询l,r,只需要使用 1 , r 1,r 1,r的线性基减去 1 , ( l − 1 ) 1,(l-1) 1,(l1)的影响即可;
那么如何去除1,(l-1)的影响;替换掉线性基里面的数,记录每一个数在原序列中的位置,如果某一位可以使用多种线性基:为了消除先后影响,一律使用最新的,即每一个位的线性基元素都应该是可选元素集合中位置最靠后面的。
换而言之:以d[i][j]表示1-i的线性基第j位;
pos[i][j]表示1-i的线性基第j位用的数本体在原序列中位置;

void _insert(int now,int k,int val)//a[loc] = k;
{
	for(int i =29;i>=0;--i){p[now][i] =p[now-1][i];loc[now][i]=loc[now-1][i];}
	for(int i =29;i>=0;--i)
	{
		if( !((1<<i)&val) )continue;
		if(!p[now][i])
		{
			p[now][i] = val;
			loc[now][i] = k;
			break;
		}
		else if(loc[now][i]<k)
		{//当前更新
			swap(loc[now][i],k);
			swap(p[now][i],val);
		}
		val^=p[now][i];
	}
}

插入函数就要做出一点改变,注意到for中多了一个loc[now][i]<k的情况:即当前位置自己本不应该插入,但是是个旧版本的线性基元素,为了让线性基有先后区别,只需要把当前数赶走就行了,来个乾坤大挪移:
swap两次把应该插入的数置换掉;
然后原序列的前缀线性基用到的数都是尽可能靠后的。那么如何消除影响:
统计区间异或最值的时候只需要跳过旧版本即可:

int query(int l,int r)
{
	int res=0;
	for(int i =29;i>=0;--i)
	{
		if(loc[r][i]>=l&&(res^p[r][i])>res)res^=p[r][i];
	}
	return res;
}

增加了一个判断:loc[r][i]>=l;

如此本题就算做完了;
总结:
前缀线性基能够维护出区间线性基,不支持删改操作,但是可以在序列后面增数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值