题意
给\(n\)堆石子,每次最多可以从一堆中取\(x\)个,问你\(x = 1 ... n\)时的答案。
解法
经典\(nim\)游戏,找规律知\(sg[i] = i \ mod \ (x+1)\)。
于是便要快速求\(a[1]\ mod\ (x+1) \bigoplus ... a[n]\ mod\ (x+1)\)。
考虑按位做,设\(y = x+1\),对于\(k \leq \frac{n}{y}\),求出\(x \in [ky,(k+1)y)\),求有多少\(x-ky\)有\(2^j\)这一位。
预处理出\(f_{j,i}\)表示对于\(x>=i\),有多少\(x-i\)有\(2^j\)这一位,有
\[f_{j,i} = f_{j,i+2^{j+1}} \ + \sum^{i+2^{j+1}-1}_{k = i+2^j} c_k
\]
其中\(c_i\)表示\(i\)出现的个数。
考虑统计答案,答案一定是几个\(2^{j+1}\)长度的整块加上一个散块,对于整块,差分即可,对于散块,发现在\([0,2^j-1]\)之间的贡献是\(0\),在\([2^j,2^{j+1}-1]\)之间是\(1\),分\((k+1)y\)在左右半边讨论即可。
code
#include<bits/stdc++.h>
#define ll long long
#define N 500015
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lowbit(i) ((i)&(-i))
#define VI vector<int>
#define all(x) x.begin(),x.end()
using namespace std;
int c[N],f[21][N];
int n;
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%d",&n);
rep(i,1,n){
int u; scanf("%d",&u); c[u] ^= 1;
}
per(i,0,n) c[i] ^= c[i+1];
rep(j,0,18){ // f(j,i) 表示 对于所有x >= i ,x-i有j这一位的异或 是0/1
per(i,0,n){
if((i+(1<<j)) > n) f[j][i] = 0; //x-i 有j这一位 -> x-i > 2^j
else if((i+(1<<(j+1))) > n) f[j][i] = c[i+(1<<j)];
else f[j][i] = f[j][i+(1<<(j+1))]^c[i+(1<<(j+1))]^c[i+(1<<j)]; //
}
}
rep(i,2,n+1){
int top = n/i;//(mod i)
bool ff = 0;
rep(j,0,18){
if((1<<j) >= i) break;
bool now = 0;
rep(k,0,top){
int l = k*i; // [k*i,(k+1)*i)
int r = l+((((i-1)>>(j+1))+1)<<(j+1)); // l ~ i-1向上第一个2^(j+1)的倍数
now ^= f[j][l];
if(r <= n) now ^= f[j][r];
now ^= c[min(max(l+i,r-(1<<j)),n+1)]^c[min(r,n+1)];
}
if(now){ff = 1; break;}
}
if(ff) printf("Alice ");
else printf("Bob ");
}
return 0;
}