ICPC模拟赛#1 A博弈

题目描述:

​ Alice 和 Bob 又要玩取石子游戏了。有 n 个房间,第 i 个房间中有 $ k_i $ 堆石子 $ (k_i≥1) $。Alice 和 Bob 轮流进行操作,Alice 先手。每次操作时,玩家可以在任何一个房间中选择任何一个非空的堆,然后从该堆中取出任意个石子。若某位玩家无法进行操作(即所有房间都是空的),则该玩家输掉游戏。

为了增加游戏的乐趣,他们新加了一个规则:在当前房间内还有石子时,不允许到其他房间内取石子。且游戏开始前给定一个长度为 n 的排列 p,表示访问顺序。当 $ p_i $ 房间内没有石子的情况下, 才可以去 $ p_i+1 $ 房间内取石子。注意,在游戏开始前,两人都知道这个排列。

在游戏开始前,Alice 可以决定房间的访问顺序。假设 Alice 和 Bob 都是最聪明的。Alice 想知道有多少种排列能使她获胜。

由于结果可能非常大,请输出答案对 \(10^9 + 7\) 取模。

输入格式:

第一行输入一个整数 T $ ( 1 ≤ T ≤ 100) $ ,表示测试的总数。

对于每个测试用例,第一行输入一个整数 n $(1 ≤ n ≤ 10^6) $ ,表示房间的总数。

接下来 n 行,每行的开始有一个整数 \(k_i\),表示该房间中有多少堆石子。接下来输入 \(k_i\) 个整数 \(a_1 , a_2 , ⋯ , a_ki (1 ≤ a ≤ 10^9 )\),表示每堆石子包含的数量。保证所有样例中 \(k_i\) 的总和不超过 \(10^6\)

输出格式:

对于每个测试,输出一个整数,表示 Alice 获胜的方案数,结果对 $ 10^9 + 7 $ 取模。

Sample Input Sample Output
2
2
1 1
1 2

4
3 1 2 3
1 1
4 1 2 3 4
5 1 2 3 4 5
1
14

前置知识:Nim游戏

n 堆物品,每堆有 \(a_i\) 个,两个玩家轮流取走任意一堆的任意个物品,但不能不取。取走最后一个物品的人获胜。

我们可以在 \(O(\prod_{i=1}^n a_i)\) 的时间里求出该局面是否先手必赢.

可以发现:

  1. 没有后继状态的状态是必败状态
  2. 对于 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n \neq 0\) 的局面,一定存在某种移动使得 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n = 0\)
  3. 对于 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n = 0\)的局面,一定不存在某种移动使得 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n = 0\)

思路:

可以发现,石子的堆数有四种情况:

  1. \(a_i\) 全为1且异或和为0(必败局面)
  2. \(a_i\) 全为1且异或和不为0(必胜局面)
  3. \(a_i\) 不全为1且异或和为0(无关局面,胜负均可)
  4. \(a_i\) 不全为1且异或和不为0 (反转局面)

并且,能否必胜只与序列前有几个房间处于反转局面有关,答案便可通过两种情况分别计算:

  1. 开头有奇数个房间处于反转局面
    即 反转房间=>必败局面=>任意布置
  2. 开头有偶数个房间处于反转局面:
    即 反转房间=>必胜局面=>任意布置

最后 ret 乘上$\ \prod_{i=n-cnt+1}^{n+1} i \ $即可

AC CODE:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,mod=1e9+7;
int t,n,k,cnt1,cnt2,cnt3,cnt4,mul[N],ret,check;
void init(){
	cnt1=0,cnt2=0,cnt3=0,cnt4=0,check=0,ret=0;
}
int ksm(int a,int b){
	int ans=1;
	while(b){
		if(b&1) ans=ans%mod*a%mod;
		a=a%mod*a%mod;
		b>>=1;
	}
	return ans;
}
int work(int a,int b){
	return mul[a]%mod*ksm(mul[b],mod-2)%mod*ksm(mul[a-b],mod-2)%mod;
}
signed main(){
	mul[0]=1;
	for(int i=1;i<N;i++) mul[i]=(mul[i-1]*i)%mod;
	cin>>t;
	while(t--){
		init();
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>k;
			bool f=1;
			check=0;
			for(int j=1;j<=k;j++){
				int x;
				cin>>x;
				check=check^x;
				if(x>1) f=0;
			}
			if(!f){
				if(check) cnt1++;
				else cnt2++;
			}
			else{
				if(check) cnt3++;
				else cnt4++;
			}
		}
		if(cnt3+cnt4==n){
			if(cnt3%2==1) cout<<mul[n]<<endl;
			else cout<<0<<endl;
			continue;
		}
		for(int i=0;i<=cnt3;i++){
			if(i%2==1) ret+=mul[i]*work(cnt3,i)%mod*cnt2%mod*mul[cnt1+cnt2-1+cnt3-i]%mod;
			else ret+=mul[i]*work(cnt3,i)%mod*cnt1%mod*mul[cnt1+cnt2-1+cnt3-i]%mod;
			ret%=mod;
		}
		for(int i=1;i<=cnt4;i++)ret=(ret*(n-cnt4+i))%mod;
		cout<<ret<<endl;
	}
	return 0;
}
posted @ 2025-09-11 16:04  kagmy_yzssy  阅读(11)  评论(0)    收藏  举报