【JZOJ3339】wyl8899和法法塔的游戏【暴力】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/3339
一个序列aa,每次给定l,r,Rl,r,R,在[l,r][l,r]中选择一个点xx,先手第一次必须选择在第RR堆石子内取。求[x,R][x,R]NIMNIM博弈先手是否有必胜策略,若有择输出第一手先手取的石子个数。
询问之间互相关联。


思路:

对于每一组询问我们枚举区间[l,r][l,r]中的ii,然后如果先手必胜,则说明先手在a[R]a[R]中选择若干石子使得a[i] xor a[i+1] xor...xor a[R]=0a[i] xor a[i+1] xor...xor a[R]=0
x=a[i] xor a[i+1] xor...xor a[R1]x=a[i] xor a[i+1] xor...xor a[R-1]。那么则要找到合适的ansans使得x xor (a[R]ans)=0x xor (a[R]-ans)=0
我们知道,有且仅有形如p xor pp xor p的情况下才可以等于0。所以我们所求即为x=a[R]ansx=a[R]-ans
由于询问直接互相关联,我们不可以用前缀异或和。只需从RR倒序循环到ll,每次询问时暴力求异或和。注意只有下标rleq r时才可以更新答案。
时间复杂度O(nm)O(nm)显然不可以过卡一卡就过去了。
正解是分块+可持久化TrieTrie


代码:

#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;
}
原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998096.html