题意
:
N
i
m
Nim
Nim 游戏,轮流从几堆石头中取出石头。在每一轮,玩家必须从一堆石头中取出一颗或多颗石头。不能拿石头的人输掉比赛。
戴安娜和爱娃可以每轮可以进行两个操作
o
p
=
=
1
op==1
op==1, 在一个区间内选出几堆进行
N
i
m
Nim
Nim 游戏,艾娃先手,戴安娜选牌堆,问戴安娜是否有机会胜利;
o
p
=
=
2
op==2
op==2, 给
[
l
,
r
]
[l,r]
[l,r] 的石头堆
a
[
i
]
a[i]
a[i] 加上
x
x
x 个石头。
题解
: N i m Nim Nim 博弈的结论是 所有 a [ i ] a[i] a[i]异或起来等于 0 0 0,先手必败。
所以
o
p
1
op1
op1 就是询问
a
[
l
.
.
.
r
]
a[l...r]
a[l...r] 内能否选出若干数使得异或为
0
0
0。
然后我们知道 线性基里面的任意一些数异或起来都不能得到 0 ,
那么问题最后转化为询问
[
l
,
r
]
[l,r]
[l,r] 的序列是否为线性基,是线性基那么先手必胜,戴安娜必败,假如不是线性基,那么就能找出任意一些数异或起来等于
0
0
0使得戴安娜胜。
o p 2 op2 op2 是区间操作可以用线段树或者树状数组差分
代码
:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6+5;
int n;
int a[N];
int c[N],b[N];
int lowbit(int x) {
return x&(-x);
}
void add(int i, int k) {
while(i<=n) {
c[i]+=k;
i+=lowbit(i);
}
}
int getsum(int i) {
int res=0;
while(i>0) {
res+=c[i];
i-=lowbit(i);
}
return res;
}
bool insert(int x) {
for(int i=30; i>=0; i--) {
if(x&(1<<i)) {
if(b[i]) x^=b[i];
else {
b[i]=x;
return 0;
}
}
}
return 1;
}
signed main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int q;
cin>>n>>q;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
while(q--) {
int op,l,r;
cin>>op>>l>>r;
if(op==1) {
int x;
cin>>x;
add(l, x);
add(r+1, -x);
}else {
bool fl=0;
for(int i=0; i<=30; i++) b[i]=0;
for(int i=l; i<=r; i++) {
if(insert(a[i]+getsum(i))) {
fl=1;
break;//因为1e9范围最长的线性基为31,所以不会t
}
}
if(fl) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
return 0;
}
总结
Qaq