大致题意: 给定一个序列,要求支持两种操作:区间修改,异或一个数;求在一段区间中选任意个数与询问值(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
",t.Qry(v));break;//询问
}return 0;
}