首页 > 其他分享 >SS241031C. 博弈(game)

SS241031C. 博弈(game)

时间:2024-10-31 21:20:39浏览次数:1  
标签:状态 博弈 SS241031C 必败 s2 son game ans gets

SS241031C. 博弈(game)

题意

博弈的规则是,有 \(3\) 个数字 \(x,y,z\),每次可以选择其中两个数字 \(x,y\),改成 \(x',y'\),满足和不变差严格变小,即 \(x+y=x'+y',|x-y|>|x'-y'|\)。无法操作的失败。给你 \(n\) 个数字,问有多少种选 \(3\) 个数字的方案使得先手必胜。

solution

首先可以设状态 \(x,y,z\) 然后爆搜 \(SG\) 函数,复杂度 \(O(V^4)\) 左右吧。

一个优化是钦定 \(x\le y\le z\)。

这种东西考虑直接打表,输出后手必败的状态,看看有没有什么规律。

赛场上我打了 \(V|3\) 的一些情况,发现对于每个 \(x\in[0,\frac{V}{3}]\),恰好有一种状态先手必败。且所有 \(x-((\frac{V}{3}-2)\bmod 4)|4\) 的 \(x\) 的先手必败的状态都是 \(x,y=\frac{V-x}{2},z=V-x-y\),以及一部分 \(x-(\frac{V}{3}\bmod 4)|4\) 的 \(x\) 也满足这种情况。然后其他状态都是形如 \(x,x,V-2x\)。

这样打表可以发现必须要存在两个相等(或者相差 \(1\)?)的数字才有必败态,特殊性质保证所有 \(a_i\) 互不相同,于是我们相信这种情况不存在必败态,直接输出 \(\binom{n}{3}\) 获得 \(15pts\)。

然而我并没有找出可靠的规律。

考虑换一种打表方式。

状态不设三维,其实两维足够了。

钦定 \(x\ge y \ge z\)。

设成 \((a,b)=(x-y,y-z)\)。

终态就是 \(a\le 1\) and \(b\le 1\) and \(a+b\le 1\)。

  1. 修改 \(x,y\):\(x'\gets x-1,y'\gets y+1,a>1 \Rightarrow (a',b')=(a-2,b+1)\)
  2. 修改 \(y,z\):\(y'\gets y-1,z'\gets z+1,b>1 \Rightarrow (a',b')=(a+1,b-2)\)
  3. 修改 \(x,z\):\(x'\gets x-1,z'\gets z+1,a+b>1 \Rightarrow (a',b')=(a-1,b-1)\)

一次操作就是执行若干次修改。

发现修改 \(1,2\) 是对称的,\(a,b\) 也是对称的,因此只考虑一种修改。

干脆钦定 \(a\ge b\)。

其中修改 \(x,z\) 如果改多了会出现 \(x<y\) 或者 \(y<z\) 的情况,也就是出现 \(b<0\)。由于我们钦定了 \(x\ge y\ge z\),因此此时再继续操作其实是在做上一种操作了。

然后根据惊人的注意力可以发现,如果 \(a,b>0\),那么状态必胜。因为你可以一直做修改 \(3\),直到 \(b=0\),如果此时的状态是必败态,就停止,否则如果此时是必胜状态,就直接模仿必胜态的操作做就可以了。

因此可以证明 \(a_i\) 互不相同的部分分。

接下来我们研究 \(b=0\) 的状态。此时你下一次操作必须使 \(a'=0\),否则会因为 \(a',b'>0\) 使得后手必胜了。

于是 \(a'=0,b'=\frac{a}{2}\)。然后因为钦定了 \(a\ge b\),因此得到 \((\frac{a}{2},0)\)。

根据刚刚的结论以及终态,可以得出 \(a\) 的二进制最低的一个 \(1\) 在从低数起偶数位上面,就是必胜状态,否则必败。

那么可以做了,我们用 \(\binom{n}{3}\) 减去必败状态的数量。

把所有数字从低位开始插入 01trie。我们需要求出状态 \(x,x,y\) 满足 \(|x-y|\) 的二进制低位起第一个 \(1\) 在奇数位的方案数。以及三个数字相等的方案数。

在 trie 上数数,遇到一个奇数高度的岔路,分左子树选两个 \(x\),右子树选一个 \(y\),和右子树选两个 \(x\),左子树选一个 \(y\) 计数。以及数三个数字相等的情况。

code

挺好写的。马蜂优良。

#include<bits/stdc++.h>
// #define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x-y;x>=z;x--)
using namespace std;
typedef long long ll;
#define isdigit(x) (x>='0'&&x<='9')
#define gc getchar_unlocked
template <typename T>
void read(T &x) {
	x=0;
	char ch=gc();
	for(;!isdigit(ch);ch=gc()) ;
	for(;isdigit(ch);ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
}
constexpr int N=5e5+7;
int t,n;
ll a[N];
ll ans;
ll C(int n,int m) {
	if(n<m) return 0;
	ll s=1;
	rep(i,0,m-1) s=s*(n-i);
	rep(i,1,m) s=s/i;
	return s;
}
ll s[N<<6];
int son[N<<6][2];
ll s2[N<<6];
int cnt;
void clear() {
	rep(i,1,cnt) s[i]=0,son[i][0]=son[i][1]=0;
	cnt=1;
}
void insert(ll x) {
	int p=1;
	rep(k,0,63){
		int op=x&1;
		if(!son[p][op]) son[p][op]=++cnt;
		p=son[p][op];
		x>>=1;
	}
	s[p]++;
}
void dfs(int u,int dep) {
	ans-=C(s[u],3);
	s2[u]=C(s[u],2);
	if(son[u][0]) dfs(son[u][0],dep+1), s[u]+=s[son[u][0]], s2[u]+=s2[son[u][0]];
	if(son[u][1]) dfs(son[u][1],dep+1), s[u]+=s[son[u][1]], s2[u]+=s2[son[u][1]];
	if(dep&1) ans-=s2[son[u][0]]*s[son[u][1]]+s2[son[u][1]]*s[son[u][0]];
}
void init(){
	clear();
	ans=0;
}
void solve() {
	init();
	ans=C(n,3);
	rep(i,1,n) insert(a[i]);
	dfs(1,1);
	pf("%lld\n",ans);
}
int main() {
	#ifdef LOCAL
	freopen("my2.out","w",stdout);
	#else 
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	#endif
	read(t);
	while(t--) {
		ans=0;
		read(n);
		rep(i,1,n) sf("%lld",&a[i]);
		solve();
	}
}

标签:状态,博弈,SS241031C,必败,s2,son,game,ans,gets
From: https://www.cnblogs.com/liyixin0514/p/18518790

相关文章