题目大意:
题目链接:https://jzoj.net/senior/#main/show/3339
一个序列
a
a
a,每次给定
l
,
r
,
R
l,r,R
l,r,R,在
[
l
,
r
]
[l,r]
[l,r]中选择一个点
x
x
x,先手第一次必须选择在第
R
R
R堆石子内取。求
[
x
,
R
]
[x,R]
[x,R]的
N
I
M
NIM
NIM博弈先手是否有必胜策略,若有择输出第一手先手取的石子个数。
询问之间互相关联。
思路:
对于每一组询问我们枚举区间
[
l
,
r
]
[l,r]
[l,r]中的
i
i
i,然后如果先手必胜,则说明先手在
a
[
R
]
a[R]
a[R]中选择若干石子使得
a
[
i
]
x
o
r
a
[
i
+
1
]
x
o
r
.
.
.
x
o
r
a
[
R
]
=
0
a[i]\ xor\ a[i+1]\ xor...xor\ a[R]=0
a[i] xor a[i+1] xor...xor a[R]=0。
设
x
=
a
[
i
]
x
o
r
a
[
i
+
1
]
x
o
r
.
.
.
x
o
r
a
[
R
−
1
]
x=a[i]\ xor\ a[i+1]\ xor...xor\ a[R-1]
x=a[i] xor a[i+1] xor...xor a[R−1]。那么则要找到合适的
a
n
s
ans
ans使得
x
x
o
r
(
a
[
R
]
−
a
n
s
)
=
0
x\ xor\ (a[R]-ans)=0
x xor (a[R]−ans)=0。
我们知道,有且仅有形如
p
x
o
r
p
p\ xor\ p
p xor p的情况下才可以等于0。所以我们所求即为
x
=
a
[
R
]
−
a
n
s
x=a[R]-ans
x=a[R]−ans。
由于询问直接互相关联,我们不可以用前缀异或和。只需从
R
R
R倒序循环到
l
l
l,每次询问时暴力求异或和。注意只有下标
≤
r
\leq r
≤r时才可以更新答案。
时间复杂度
O
(
n
m
)
O(nm)
O(nm)。显然不可以过卡一卡就过去了。
正解是分块+可持久化
T
r
i
e
Trie
Trie。
代码:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <cstdio>
#include <string>
#include <algorithm>
#define reg register
using namespace std;
const int N=100010,Inf=2e9;
int n,T,ans,l,r,R,x,a[N];
int read()
{
int d=0;
char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+48);
}
int main()
{
n=read();
for (reg int i=1;i<=n;i++)
a[i]=read();
T=read();
while (T--)
{
R=read(); l=read(); r=read();
ans=Inf; x=0;
if (!a[R]) puts("-1");
else if (R==r) write(a[r]),putchar(10),a[r]=0;
else
{
for (reg int i=R-1;i>=l;i--)
{
x^=a[i];
if (i<=r&&x<ans) ans=x;
}
if (a[R]-ans>0) write(a[R]-ans),putchar(10),a[R]=ans;
else puts("-1");
}
}
return 0;
}