[海军国际项目办公室]石子游戏

石子游戏

题解

5 × 1 0 5 5\times10^5 5×105的范围 O ( n log ⁡ 2   n ) O\left(n\log^2\,n\right) O(nlog2n)就离谱,还只开 1 s 1s 1s

看到这道题,我们应该很容易联想到 N i m Nim Nim游戏的方法,我们可以通过 s g sg sg函数来得到答案。
我们定义 s g i sg_{i} sgi表示对于数量为 i i i的石子堆的 s g sg sg函数值。
显然, s g i = m e x j = i − x i − 1 s g j sg_{i}=mex_{j=i-x}^{i-1}sg_{j} sgi=mexj=ixi1sgj,显然,答案当 x o r i = 1 n s g a i xor_{i=1}^{n}sg_{a_{i}} xori=1nsgai大于 0 0 0时是先手必胜的,否则是后手必胜的。
关于这个的证明可以自己去了解一下 N i m Nim Nim游戏。

而我们要求的是对于 x = 1 , 2 , . . . , n x=1,2,...,n x=1,2,...,n的所有情况的异或值。
我们考虑我们的 s g sg sg函数与 x x x有什么关系。很容易发现, s g i = ( i m o d     ( x + 1 ) ) sg_{i}=(i\mod\,(x+1)) sgi=(imod(x+1))
所以,我们的答案应该是 x o r i = 1 n ( a i m o d     ( x + 1 ) ) xor_{i=1}^{n}(a_{i}\mod\,(x+1)) xori=1n(aimod(x+1))
事实上只有出现奇数次的 a i a_{i} ai会有贡献,我们记 a i a_{i} ai出现次数的奇偶性为 c n t a i cnt_{a_{i}} cntai,那么答案应为 x o r i = 1 n ( c n t i i m o d     ( x + 1 ) ) xor_{i=1}^{n}(cnt_{i}i\mod\, (x+1)) xori=1n(cntiimod(x+1))
也就是说,我们每次只需要将出现奇数次的数模 x + 1 x+1 x+1的异或和求来就行了。
x x x又会从 1 1 1 n n n连续变化,考虑怎么求出这些答案。
它们都是模数在不断连续变化,我们很容易联想到Maximize GCD,通过调和级数来使我们的所有的枚举复杂度达到 log ⁡   n \log\,n logn

但这样也就要求快速求出一个区间里的异或和,但这个区间内并非每个数都有贡献。
由于这是异或的转移,很容易从二进制位上找到方向, [ 0 , 1 , . . . , 2 j − 1 − 1 ] [0,1,...,2^{j-1}-1] [0,1,...,2j11] [ 2 j − 1 , 2 j − 1 + 1 , . . . , 2 j ] [2^{j-1},2^{j-1}+1,...,2^{j}] [2j1,2j1+1,...,2j]的区别仅在于 2 j − 1 2^{j-1} 2j1位上是 0 0 0还是 1 1 1
所以我们可以通过倍增来快速求出答案。
我们定义 f i , j f_{i,j} fi,j表示从 i i i开始,也就是 i i i的贡献为 1 1 1,往后延伸 2 j 2^j 2j位,贡献不断增加,最后总的异或和的大小。
很明显, f f f的转移是与普通倍增的转移相似的,最多只需要判断后半部分的数的数量,如果是奇数就异或上 2 j − 1 2^{j-1} 2j1
同样,对于每个数的查询我们也是一段一段跳着去查询这一段的异或和,每次跳到模 ( x + 1 ) (x+1) (x+1) 1 1 1的位置,往后 x x x个的异或和。
同样地,这 x x x个也可以用上面的方法求出,我们从 2 log ⁡   x 2^{\log\,x} 2logx开始往下找,跳过去,后面跳的肯定包含我们前面跳的部分的二进制位,我们的步长是在逐渐缩小的,所以我们后面的会包含前面,同样通过奇偶性判断就行了。
每个 x x x会进行 ⌈ n x ⌉ \lceil\frac{n}{x}\rceil xn次倍增的操作,根据调和级数,最后进行的倍增操作次数是 n ln ⁡   n n\ln\,n nlnn的。

时间复杂度 O ( n ln ⁡   n log ⁡   n ) O\left(n\ln\,n\log\,n\right) O(nlnnlogn)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define MAXN 500005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;     
const LL INF=0x3f3f3f3f3f3f3f3f;  
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
char gc(){static char buf[5000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
#define getchar gc
char obuf[1<<22],*opt=obuf+(1<<22);
void pc(const int&ch){*--opt=ch;}
#define putchar pc
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,a[MAXN],cnt[MAXN],sum[MAXN],f[MAXN][22],ans,lg[MAXN],hig[MAXN];
signed main(){
	freopen("stone.in","r",stdin);
	freopen("stone.out","w",stdout);
	read(n);for(reg int i=1;i<=n;i++)read(a[i]),cnt[a[i]]^=1;
	for(reg int i=1;i<=n;i++)sum[i]=sum[i-1]+cnt[i];
	for(reg int i=1;i<=n;i++)f[i][0]=cnt[i];
	for(reg int i=2;i<=n;i++)lg[i]=lg[i>>1]+1,hig[i]=hig[i>>1]+1;
	for(reg int j=1;j<=lg[n];j++)
		for(reg int i=1;i<=n-(1<<j)+1;i++){
			f[i][j]=f[i][j-1]^f[i+(1<<j-1)][j-1];
			if(cnt[i+(1<<j)-1])f[i][j]^=(1<<j-1),f[i][j]^=(1<<j);
			if(sum[i+(1<<j)-2]-sum[i+(1<<j-1)-1]&1)f[i][j]^=(1<<j-1);
		}
	for(reg int i=n;i>0;i--){
		ans=0;
		for(reg int j=1;j<=n;j+=(i+1)){
			reg int tp=0,x=j,len=min(i,n-j+1);
			for(reg int k=len;k;){
				const int t=(1<<hig[k]);
				if(sum[x+t-1]-sum[x-1]&1)ans^=tp;
				ans^=f[x][hig[k]];tp^=t;x+=t;k^=t;
			}
		}
		if(i==n)pc('\n');else pc(' ');
		if(ans)pc('e'),pc('c'),pc('i'),pc('l'),pc('A');
		else pc('b'),pc('o'),pc('B');	
	}
	fwrite(opt,1,obuf+(1<<22)-opt,stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值