题解 奇怪的博弈

传送门

先将黑色石子形成的游戏独立出来

打表或模拟递推 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;
}
posted @ 2022-05-25 08:29  Administrator-09  阅读(1)  评论(0编辑  收藏  举报