\(\cal T_1\) 奇怪的博弈
Description
小 \(\rm Al\) 和小 \(\rm Ba\) 想要玩一局取石子游戏,但是他们不想变得和 \(\text{Alice & Bob}\) 一样,于是他们研究出了一种奇怪的取石子游戏:
- 总共有 \(n\) 堆石子,第 \(i\) 堆的石子数量为 \(a_i\);
- 和普通的取石子游戏不同的是 —— 每堆石子都有颜色,要么是黑色要么是白色;
一个玩家在一轮中需要进行以下两种操作之一:
- 选择一个白色石子堆,取出至少一个石子;
- 若选择黑色石子堆,则只能选择当前黑色石子堆中石子数量最少的(如果有多个则任选一个),取出至少一个石子;
第一个把石子取完的玩家获胜。
小 \(\rm Al\) 和小 \(\rm Ba\) 已经迫不及待地准备好了这 \(n\) 堆石子,但是他们还没有给石子着色。小 \(\rm Ba\) 慷慨地把先手让给了 \(\rm Al\);作为交换,由小 \(\rm Ba\) 决定每堆石子的颜色。
小 \(\rm Ba\) 想知道有多少种染色的方案使小 \(\rm Ba\) 必胜。你可以认为小 \(\rm Al\) 和小 \(\rm Ba\) 都是绝对聪明的。
另外,小 \(\rm Ba\) 非常确信答案可以很大,所以他只要求你输出它对 \(10^9+7\) 取模的结果。
\(1\le n\le 10^5,1\le a_i\le 10^{18}\).
Solution
对于白色石子可以直接算出所有子游戏的 \(\rm sg\) 值,对于黑色石子,显然无法划分成若干个独立子游戏考虑,所以不妨考虑总局面的 \(\rm sg\) 值(记 \(\text{sg}(s_1,s_2,\dots, s_k)\) 表示 \(k\) 堆石子大小分别为 \(s_1,s_2,\dots,s_k\) 且钦定 \(s_1\le s_2\le \dots \le s_k\) 的 \(\rm sg\) 值):
这里是自己的手动推导过程。
s(0,k)=k, s(1,k)=0, s(2,k)=1, s(3,k)=2, s(a,b)=a-1
s(0,b,c)=b-1, s(1,b,c) = if(b=1) then 1; else then 0; s(2,b,c) = if(b=2) then 2; else then 1;
s(3,b,c) = if(b=3) then 3; else 2; s(a,b,c) = if(b=a) then a; else then a-1;
s(0,a,b,c) = if(b=a) then a; else then a-1;
s(1,a,b,c) = if(b=a) then 0; else if(a=1) then 1; else 0;
s(2,a,b,c) = if(b=a) then 1; else if(a=2) then 2; else 1;
s(3,a,b,c) = if(b=a) then 2; else if(a=3) then 3; else 2;
s(d,a,b,c) = if(b=a || a!=d) then d-1; else d;
可以推出一个结论:
\(\mathcal{C}\text{onclusion}\):记最小的黑色堆大小为 \(m\),且有 \(c\) 个,则黑色堆的子游戏的 \(\rm sg\) 函数值为
\[m-((c\bmod 2)\oplus [\text{all the black piles have the same size}]) \]
于是枚举最小的黑色堆大小 \(m\),对所有黑色堆是否同一种大小进行分类讨论。对于不同种大小的情况,相当于求大于 \(m\) 的石子堆中,求有多少种组合可以使得与 \(\rm val\) 的异或和为零(\(\rm val\) 就是此时黑色堆 \(\rm sg\) 值异或小于 \(m\) 的石子堆的异或和[1])。可以使用线性基求解,若在大于 \(m\) 的石子堆中,有 \(p\) 个石子堆无法插入线性基,当线性基可以异或出 \(\rm val\) 时,就新增了 \(2^p\) 种方案。复杂度 \(\mathcal O(n\log V)\).
Code
还蛮细节的,反反复复 \(\rm wa\) 了好几次。
# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while(!isdigit(s=getchar())) f|=(s=='-');
for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
return f? -x: x;
}
template <class T>
inline void write(T x) {
static int writ[50], w_tp=0;
if(x<0) putchar('-'), x=-x;
do writ[++w_tp]=x-x/10*10, x/=10; while(x);
while(putchar(writ[w_tp--]^48), w_tp);
}
# include <algorithm>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn = 1e5+5;
inline int inv(int x,int y=mod-2,int r=1) {
for(; y; y>>=1, x=1ll*x*x%mod)
if(y&1) r=1ll*r*x%mod;
return r;
}
inline int dec(int x,int y) { return x-y<0?x-y+mod:x-y; }
ll a[maxn],d[64],pre[maxn];
int n,fail,pw[maxn],fac[maxn],ifac[maxn];
inline void ins(ll x) {
for(int i=63;i>=0;--i) if(x>>i&1) {
if(d[i]) x ^= d[i];
else return d[i]=x, void();
}
++ fail;
}
inline bool inside(ll x) {
if(!x) return true;
for(int i=63;i>=0;--i) if(x>>i&1) {
if(d[i]) x ^= d[i];
else return false;
}
return true;
}
inline void initialize() {
for(int i=fac[0]=1;i<=n;++i)
fac[i] = 1ll*fac[i-1]*i%mod;
ifac[n] = inv(fac[n]);
for(int i=n-1;i>=0;--i)
ifac[i] = 1ll*ifac[i+1]*(i+1)%mod;
}
inline int C(int n,int m) {
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int main() {
freopen("nim.in","r",stdin);
freopen("nim.out","w",stdout);
n=read(9); initialize();
for(int i=pw[0]=1;i<=n;++i)
a[i]=read(9ll),
pw[i] = 2ll*pw[i-1]%mod;
sort(a+1,a+n+1); ll ans=0, suf=0 ,sg;
for(int i=1;i<=n;++i)
pre[i] = pre[i-1]^a[i];
if(!pre[n]) ++ ans;
for(int i=n,j; i; ) {
for(j=i; a[j]==a[i]; --j); ++j;
for(int c=1;c<=i-j+1;++c) {
sg = pre[j-1]^(a[i]-(c&1))^((i-j+1-c&1)?a[i]:0);
if(inside(sg))
ans += 1ll*dec(pw[fail],(sg^suf)==0)*C(i-j+1,c)%mod;
sg = pre[j-1]^(a[i]-((c&1)^1))^((i-j+1-c&1)?a[i]:0);
if((sg^suf)==0) ans += C(i-j+1,c);
}
ans %= mod;
for(; i>=j; --i) suf^=a[i], ins(a[i]);
} print(ans%mod,'\n');
return 0;
}
\(\cal T_2\)
Description
Solution
Code
\(\cal T_3\)
Description
Solution
Code
小于 \(m\) 的石子堆一定是白色的。 ↩︎
浙公网安备 33010602011771号