题解 奇怪的博弈
先将黑色石子形成的游戏独立出来
打表或模拟递推 SG 的过程发现
# 结论
记最小的黑色堆大小为 \(m\),且有 \(c\) 个,则黑色堆的子游戏的 SG 函数值为:
\[m - \big((c \bmod 2) \oplus [所有黑色堆大小相同]\big) \]
嗯 markdown 版本的题解真方便
证明考虑若只有一堆,有 \(i\) 个则 \(SG=i-1\)
每一堆数量 \(\leqslant\) 下一堆,所以仅在这一堆数量 = 下一堆数量时 SG 值于相等的堆的数量有关
于是排序后枚举每种最小石子堆,枚举选多少个涂黑
则问题变为对可重集求异或和为某个值的方案数
- 对可重集求异或和为某个值的方案数:建出线性基,若能异或出这个值则方案数为 \(2^{|未能插入线性基的元素|}\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline ll read() {
ll ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
ll a[N];
const ll mod=1e9+7;
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
namespace force{
ll ans;
map<pair<vector<int>, vector<int>>, bool> mp;
bool dfs(pair<vector<int>, vector<int>> s) {
if (!s.fir.size()&&!s.sec.size()) return 0;
if (mp.find(s)!=mp.end()) return mp[s];
bool any_lose=0;
for (int i=0; i<s.fir.size(); ++i) {
for (int j=1; j<=s.fir[i]; ++j) {
pair<vector<int>, vector<int>> t=s;
int pos=t.fir.size()-1;
swap(t.fir[i], t.fir[pos]);
if (!(t.fir[pos]-=j)) t.fir.pop_back();
sort(t.fir.begin(), t.fir.end());
if (!dfs(t)) {any_lose=1; return mp[s]=1;}
}
}
if (s.sec.size()) {
for (int j=1; j<=s.sec.back(); ++j) {
pair<vector<int>, vector<int>> t=s;
int pos=t.sec.size()-1;
if (!(t.sec[pos]-=j)) t.sec.pop_back();
if (!dfs(t)) {any_lose=1; return mp[s]=1;}
}
}
return mp[s]=0;
}
void solve() {
int lim=1<<n;
for (int s=0; s<lim; ++s) {
pair<vector<int>, vector<int>> t;
for (int i=1; i<=n; ++i)
if (s&(1<<i-1)) t.sec.pb(a[i]);
else t.fir.pb(a[i]);
sort(t.fir.begin(), t.fir.end());
sort(t.sec.begin(), t.sec.end(), [](int a, int b){return a>b;});
ans+=!dfs(t);
}
printf("%lld\n", ans);
}
}
namespace task{
int s;
ll suf[N], pre[N], fac[N], inv[N], ans;
inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
struct basic{
ll base[65];
void ins(ll val) {
for (int i=63; ~i; --i) if (val&(1ll<<i)) {
if (!base[i]) {base[i]=val; return ;}
else val^=base[i];
}
++s;
}
bool check(ll val) {
for (int i=63; ~i; --i) if (val&(1ll<<i)) {
if (!base[i]) return 0;
else val^=base[i];
}
return 1;
}
}bas;
void solve() {
fac[0]=fac[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<=n; ++i) inv[i]=inv[i-1]*inv[i]%mod;
sort(a+1, a+n+1, [](ll a, ll b){return a>b;});
for (int i=n; i; --i) suf[i]=suf[i+1]^a[i];
for (int i=1; i<=n; ++i) pre[i]=pre[i-1]^a[i];
for (int p1=1,p2; p1<=n; p1=p2) {
p2=p1;
while (p2<=n&&a[p1]==a[p2]) ++p2;
int siz=p2-p1;
for (int i=1; i<=siz; ++i) {
ll sg=(a[p1]-(i&1))^suf[p1+i];
// cout<<"sg: "<<sg<<endl;
if (bas.check(sg)) ans=(ans+C(siz, i)*(qpow(2, s)-(pre[p1-1]==sg)))%mod;
if (pre[p1-1]==((a[p1]-((i&1)^1))^suf[p1+i])) ans=(ans+C(siz, i))%mod;
}
for (int i=1; i<=siz; ++i) bas.ins(a[p1]);
}
if (!pre[n]) ans=(ans+1)%mod;
printf("%lld\n", (ans%mod+mod)%mod);
}
}
signed main()
{
freopen("nim.in", "r", stdin);
freopen("nim.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) a[i]=read();
// force::solve();
task::solve();
return 0;
}