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)\) 的时间里求出该局面是否先手必赢.
可以发现:
- 没有后继状态的状态是必败状态
- 对于 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n \neq 0\) 的局面,一定存在某种移动使得 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n = 0\)
- 对于 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n = 0\)的局面,一定不存在某种移动使得 \(a_1 \oplus a_2 \oplus \ldots \oplus a_n = 0\)
思路:
可以发现,石子的堆数有四种情况:
- \(a_i\) 全为1且异或和为0(必败局面)
- \(a_i\) 全为1且异或和不为0(必胜局面)
- \(a_i\) 不全为1且异或和为0(无关局面,胜负均可)
- \(a_i\) 不全为1且异或和不为0 (反转局面)
并且,能否必胜只与序列前有几个房间处于反转局面有关,答案便可通过两种情况分别计算:
- 开头有奇数个房间处于反转局面
即 反转房间=>必败局面=>任意布置 - 开头有偶数个房间处于反转局面:
即 反转房间=>必胜局面=>任意布置
最后 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;
}
本文来自博客园,作者:kagmy_yzssy,转载请注明原文链接:https://www.cnblogs.com/yzssy/p/19086161

浙公网安备 33010602011771号