题解 Luogu P5607 [Ynoi2013] 无力回天 NOI2017
题意
给定长度为 的整数序列 ,要求支持 次操作,每次操作是以下两种之一:
- 给定 ,将区间 异或上
- 给定 ,查询区间 内选出任意个数的 ,这些数和 的最大异或和(可以不选)
题解
注意到区间异或之后可以维护的信息非常有限,考虑差分,记 。
那么区间异或相当于把 异或上 。
考虑查询,可以用 0-1 Trie 或者线性基。但是区间 0-1 Trie 肯定存不下,于是只能线性基。
然后有一个聪明的性质, 的线性基和 相同。
稍加观察就可以发现右边能表出的数一定是若干个 异或起来,所以右边能被左边表出。
左边能表出的是若干个 的异或和,那么只要能够证明右边可以表出任意一个 ,就可以证明右边可以表出任意个 的异或和。我们只需要把表出单个 的方案拼到一起,最后只留下在方案中出现了奇数次的数,就得到了表出这些 的方案。
事实上这是非常简单的。对于 ,就选 。对于后面的,就是选 和一段 异或起来。
然后?维护区间线性基,支持单点修改区间查询,因为两个线性基合并是 的,于是复杂度是 。
一个线性基小技巧:合并两个线性基的正确姿势是在一个里面插入另外一个,而不是建一个新的线性基憨憨地把两边都插进去,常数大到飞起。
甚至还有一种更优秀的写法。
inline void merge(Base &cur,Base lc,Base rc){
cur.clear();
for(int i=30;i>=0;--i)
if(lc.a[i]) cur.a[i]=lc.a[i];
else cur.a[i]=rc.a[i];
for(int i=30;i>=0;--i) if(lc.a[i]&&rc.a[i]) cur.ins(rc.a[i]);
return;
}
即:先尝试只用左边或右边的数表出某些位,对于那些两边都有值的位,再尝试两边的数都用,看能不能表出更多。
# include <bits/stdc++.h>
const int N=50010,INF=0x3f3f3f3f;
struct Base{
int a[32];
inline void ins(int x){
for(int i=30;i>=0;--i){
if(x&(1<<i)){
if(a[i]) x^=a[i];
else{
a[i]=x;
break;
}
}
}
return;
}
inline void clear(void){
memset(a,0,sizeof(a));
return;
}
}tree[N<<2];
int xsum[N<<2];
int a[N],b[N];
int n,m;
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
inline int lc(int x){
return x<<1;
}
inline int rc(int x){
return x<<1|1;
}
inline void merge(Base &cur,Base lc,Base rc){
cur.clear();
for(int i=30;i>=0;--i)
if(lc.a[i]) cur.a[i]=lc.a[i];
else cur.a[i]=rc.a[i];
for(int i=30;i>=0;--i) if(lc.a[i]&&rc.a[i]) cur.ins(rc.a[i]);
return;
}
inline void pushup(int k){
merge(tree[k],tree[lc(k)],tree[rc(k)]);
xsum[k]=xsum[lc(k)]^xsum[rc(k)];
return;
}
void build(int k,int l,int r){
if(l==r){
tree[k].ins(b[l]),xsum[k]=b[l];
return;
}
int mid=(l+r)>>1;
build(lc(k),l,mid),build(rc(k),mid+1,r);
pushup(k);
return;
}
void change(int k,int l,int r,int x,int v){
if(l==r){
tree[k].clear(),xsum[k]=(b[l]^=v),tree[k].ins(b[l]);
return;
}
int mid=(l+r)>>1;
if(x<=mid) change(lc(k),l,mid,x,v);
else change(rc(k),mid+1,r,x,v);
pushup(k);
return;
}
int queryval(int k,int l,int r,int L,int R){
if(L<=l&&r<=R) return xsum[k];
int mid=(l+r)>>1,res=0;
if(L<=mid) res^=queryval(lc(k),l,mid,L,R);
if(mid<R) res^=queryval(rc(k),mid+1,r,L,R);
return res;
}
Base querybase(int k,int l,int r,int L,int R){
if(L>R){
return tree[0];
}
if(L<=l&&r<=R) return tree[k];
int mid=(l+r)>>1;
Base res;
if((L<=mid)&&!(mid<R)) return querybase(lc(k),l,mid,L,R);
if(!(L<=mid)&&mid<R) return querybase(rc(k),mid+1,r,L,R);
merge(res,querybase(lc(k),l,mid,L,R),querybase(rc(k),mid+1,r,L,R));
return res;
}
int main(void){
n=read(),m=read();
for(int i=1;i<=n;++i) a[i]=read(),b[i]=a[i]^a[i-1];
build(1,1,n);
int op,l,r,v;
while(m--){
op=read(),l=read(),r=read(),v=read();
if(op==1){
change(1,1,n,l,v);
if(r<n) change(1,1,n,r+1,v);
}else{
Base res=querybase(1,1,n,l+1,r);
int al=queryval(1,1,n,1,l);
res.ins(al);
for(int i=30;i>=0;--i) v=std::max(v,v^res.a[i]);
printf("%d\n",v);
}
}
return 0;
}
作者:Meatherm
出处:https://www.cnblogs.com/Meatherm/p/16546595.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!