正题
题目大意
有nnn堆石子,每次选择一个区间博弈,先手必须先取最右边的石子堆。
每次询问(r,a,b)(r,a,b)(r,a,b)表示在a∼ba\sim ba∼b中选择一个数lll。要求使用l∼rl\sim rl∼r这个区间的石子进行博弈,然后若先手必胜输出最右边的石子需取的最大石子个数并让石子堆减去这个数。若先手必败输出−1-1−1
解题思路
首先这是一个NIMNIMNIM博弈,若alxoral+1xoral+2xor...xorar=0a_l\ xor\ a_{l+1}\ xor\ a_{l+2}\ xor...\ xor\ a_r=0al xor al+1 xor al+2 xor... xor ar=0那么先手必败。
那如果alxoral+1xoral+2xor...xorar−1=xa_l\ xor\ a_{l+1}\ xor\ a_{l+2}\ xor...\ xor\ a_{r-1}=xal xor al+1 xor al+2 xor... xor ar−1=x且x≤arx\leq a_rx≤ar那么我们可以从ara_rar中取走ar−xa_r-xar−x个石头然后使得ar=xa_r=xar=x那么这样轮到后手开始时所以的异或起来就为0了。
那么我们如何求最小值呢?分块套Trie暴力!
因为数据水所以能够过,不过需要加两个优化
- 当b=rb=rb=r时直接让lll取rrr,这个十分显然,因为要使异或和最小所以自己异或自己就是0
- 当ar=0a_r=0ar=0时输出-1,依旧十分显然
日后可能会将正解补上有生之年
codecodecode
#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110000;
int n,w[N],m;
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&w[i]);scanf("%d",&m);while(m--){int x=0,ans=2147483647,a,b,r;scanf("%d%d%d",&r,&a,&b);if(w[r]==0){printf("-1\n");continue;}if(r==b){printf("%d\n",w[r]);w[r]=0;continue;}for(int i=r-1;i>=a;i--){x=x^w[i];if(i<=b) ans=min(ans,x);}if(w[r]-ans>0) printf("%d\n",w[r]-ans),w[r]=ans;else printf("-1\n");}
}