首页 > 其他 > 详细

【洛谷5607】[Ynoi2013] 无力回天NOI2017(线段树套线性基)

时间:2020-05-11 20:50:23      阅读:39      评论:0      收藏:0      [点我收藏+]

点此看题面

大致题意: 给定一个序列,要求支持两种操作:区间修改,异或一个数;求在一段区间中选任意个数与询问值\(v\)异或得到的最大值。

前言

本来想学一学\(KD-Tree\),结果不知道为什么\(T\)得很惨很惨,调了很久都没能调出来。

心态爆炸,于是默默点开了Ynoi。。。

看看,这Ynoi多良心,三个\(log\)堆在这里都\(T\)不掉。不过就是个线段树套线性基嘛,比\(KD-Tree\)好写多了,甚至还不用写什么噼(pia)里(pia)啪(pia)啦(pia)的重构。

无力回天,如此弱小的我,恐怕是真的无力回天了啊!

有那么一刻,我回想起了去年\(CSP\)的绝望,那连盘都摸不到的可怕。尽管现在看来去年CSP没有任何用处。

有那么一刻,我回想起了曾经的一场场模拟赛,毫无悬念地被吊锤。纵使日薄西山,即便看不到未来,此时此刻的我,依旧无法改变蒟蒻的本质。

有那么一刻,我回想起了那道名叫希望的题目,容斥+长链剖分+回退数据结构+离线求逆元,四倍役满,四倍退役。那就是希望,即便需要取模,也是光明。

有那么一刻,我回想起了猪国杀,被大模拟所支配的日日夜夜,徘徊于冗长代码间的一天又一天。

有那么一刻,我回想起了\(AT678\)尽管我没做过。因为此题是道非常残忍的题,可以的话我认为你应该先开心地做其他题,如果其他题做完了还有时间的话,就以此题为消遣吧。

有那么一刻,我回想起了。。。

怎么突然有种退役既视感,感觉自己像在写退役后的OI生涯总结。。。

实际上只是听着窗外放的歌,看着这道题目的名称,心情突然激动起来了。。。

咳咳,好,现在我们回到正题。

线性基

看看这道题的询问,你想到了什么?

显然是线性基的经典操作嘛!

这其实也就启示了我们,只要想办法维护出一段区间的线性基,我们就能轻松地实现询问了。

可是,维护一段区间的线性基,又谈何容易?

让我们来看看题面,区间修改?带修线性基?线性基还能这么玩?

于是一脸懵逼的我开始了满脑子懵逼的思考,最后懵逼地无力回天。事实上,直至懵逼地点开题解前懵逼的那一刻,懵逼的我都始终保持着无比懵逼的状态。咳咳,又扯远了。

线段树套线性基

考虑是要维护一段区间的线性基,而我们知道,线性基的合并是可以在\(O(log^2V)\)的复杂度内实现的。

而且,修改一般的线性基是非常困难的,但如果修改只有一个元素的线性基呢?

好了,话都说到这里了,不难明白,我所说的,正是线段树套线性基。

这样的复杂度是\(O(NlogNlog^2V)\)的。(\(CJJ\)\(log\)\(T\)的哒?)

等等,这样子似乎还是不能实现区间修改啊?

因此,我们需要把区间修改转化为单点修改。

怎么转化呢?这就需要利用异或这种神奇运算的神奇性质了。

我们令\(s_i=a_i\ xor\ a_{i-1}\),然后我们发现,对于\(i=l+1,l+2,...,r\)\(s_i\)都不改变。而发生变化的仅仅是\(s_l\)\(s_{r+1}\)两个值而已,这样一来就把区间修改转化为了单点修改。

再等等,这么一搞之后,还怎么处理询问呢?

然后我们发现,\(\{a_l,a_{l+1},...,a_r\}\)\(\{a_l,s_{l+1},s_{l+2},...,s_r\}\)二者的线性基是等价的。

为什么?因为\(s_i=a_i\ xor\ a_{i-1}\),所以后者其实还是\(\{a_l,a_{l+1},...,a_r\}\)这堆东西在异或来异或去,并没有少掉谁,也没有多冒出来谁。

所以,我们用线段树维护每个区间内\(s_i\)的线性基,询问时求出\(\{s_{l+1},s_{l+2},...,s_r\}\)的线性基,然后往里面扔入一个\(a_l\),我们就可以愉快地询问了。

最后一次等等,这样不是还要维护\(a_l\)吗?

是的,不过如果我们维护\(s_i\),然后就会发现\(xor_{i=1}^ls_i=(xor_{i=1}^la_i)\ xor\ (xor_{i=1}^{l-1}a_i)=a_l\)

也就是说,只要实现单点修改以及求前缀和就可以了,树状数组显然可以很优秀地完成这项任务。

写到这里,突然感觉维护线性基的线段树也可以直接借来做这个东西,反正都已经维护s了?算了,管它呢,反正写都写了,过都过了,还有啥好改的。

具体实现详见代码。

代码

#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 50000
#define LV 30
using namespace std;
int n,a[N+5];
class LinearBasis//线性基
{
	private:
		#define LB LinearBasis
		int v[LV+5];
	public:
		I LB() {for(RI i=LV;~i;--i) v[i]=0;}
		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 x) {for(RI i=LV;~i;--i) (x^v[i])>x&&(x^=v[i]);return x;}//查询
		I friend LB operator + (LB x,LB y) {for(RI i=LV;~i;--i) y.v[i]&&(x.Ins(y.v[i]),0);return x;}//合并
};
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 
		#define PU(x) (S[x]=S[x<<1]+S[x<<1|1])
		int V[N<<2];LB S[N<<2];
	public:
		I void Build(int *a,PT)//建树
		{
			if(l==r) return S[rt].Ins(V[rt]=a[l]^a[l-1]);int mid=l+r>>1;
			Build(a,LT),Build(a,RT),PU(rt);
		}
		I void U(CI x,CI v,PT)//单点修改
		{
			if(l==r) return (S[rt]=LB()).Ins(V[rt]^=v);int mid=l+r>>1;
			x<=mid?U(x,v,LT):U(x,v,RT),PU(rt);
		}
		I LB Q(CI x,CI y,PT)//求出一段区间的线性基
		{
			if(x<=l&&r<=y) return S[rt];int mid=l+r>>1;
			if(y<=mid) return Q(x,y,LT);if(x>mid) return Q(x,y,RT);
			return Q(x,mid,LT)+Q(mid+1,y,RT);
		}
}S;
class TreeArray//树状数组
{
	private:
		int a[N+5];
	public:
		I void U(RI x,CI v) {W(x<=n) a[x]^=v,x+=x&-x;}//单点修改
		I int Q(RI x,RI t=0) {W(x) t^=a[x],x-=x&-x;return t;}//求前缀和
}T;
int main()
{
	RI Qt,i,op,x,y,v;scanf("%d%d",&n,&Qt);
	for(i=1;i<=n;++i) scanf("%d",a+i),T.U(i,a[i]^a[i-1]);
	LB t;S.Build(a);W(Qt--) switch(scanf("%d%d%d%d",&op,&x,&y,&v),op)
	{
		case 1:S.U(x,v),T.U(x,v),y^n&&(S.U(y+1,v),T.U(y+1,v),0);break;//修改
		case 2:(t=x^y?S.Q(x+1,y):LB()).Ins(T.Q(x)),printf("%d\n",t.Qry(v));break;//询问
	}return 0;
}

【洛谷5607】[Ynoi2013] 无力回天NOI2017(线段树套线性基)

原文:https://www.cnblogs.com/chenxiaoran666/p/Luogu5607.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!