1. 程式人生 > 實用技巧 >【洛谷P5675】取石子游戲

【洛谷P5675】取石子游戲

題目

題目連結:https://www.luogu.com.cn/problem/P5675
Alice 和 Bob 在玩一個古老的遊戲。現在有若干堆石子,Alice 和 Bob 輪流取,每次可以選擇其中某一堆的石子中取出任意顆石子,但不能不取,誰先取完使得另一個人不能取了算贏。

現在場地上有\(N\)堆石子,編號為 \(1\)\(N\)。Alice 很快發現了這個遊戲存在一些固定的策略。陰險的 Alice 想贏得這場比賽就來找到主辦方你,希望你在這 \(N\) 堆石子中選出若干堆石子作為最後遊戲用的石子堆並使得 Alice 能獲得勝利。你自然不想讓 Alice 得逞,所以你提出了一個條件:Alice 在這個遊戲中第一次取的那堆石子的編號需要你來指定(僅指定取的石子堆編號,不指定第一次取多少個,這個指定的石子堆必然包含在最後遊戲用的石子堆中)。

現在你很好奇,你想算算有多少種方案讓 Alice 不能獲勝。注意,即使選出的石子堆的編號的集合完全相同,指定第一次取的石子堆的編號不同,也認為方案是不同的。

思路

\(f[i][j]\) 表示前 \(i\) 個數字取了若干個異或和為 \(j\) 的方案數,\(g[i][j]\) 表示第 \(i\) 個數以後取若干個數異或和為 \(j\) 的方案數。
列舉每一個位置作為選擇的石子堆,假設其他石子的異或和為 \(k\),那麼我們只需要滿足無論這堆石子取多少,都有 \(k \operatorname{xor}\) 取的石子數 \(\neq 0\)。也就是需要滿足 \(k\geq a_i\)
直接無腦 \(O(nA^2)\)

統計答案即可。其中 \(A\) 表示 \(a\) 的上限。

程式碼

#include <bits/stdc++.h>
using namespace std;

const int N=260,MOD=1e9+7;
int n,ans,a[N],f[N][N],g[N][N];

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	f[0][0]=g[n+1][0]=1;
	for (int i=1;i<=n;i++)
		for (int j=0;j<=255;j++)
			f[i][j]=(f[i-1][j]+f[i-1][j^a[i]])%MOD;
	for (int i=n;i>=1;i--)
		for (int j=0;j<=255;j++)
			g[i][j]=(g[i+1][j]+g[i+1][j^a[i]])%MOD;
	for (int i=1;i<=n;i++)
		for (int j=0;j<=255;j++)
			for (int k=0;k<=255;k++)
				if ((j^k)>=a[i])
					ans=(ans+1LL*f[i-1][j]*g[i+1][k])%MOD;
	printf("%d",ans);
	return 0;
}