“蔚来杯“2022牛客暑期多校训练营2
觉得有帮助的点个赞!
C Link with Nim Game
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int T, n, m;
int a[N]; //记录石子值
int num; //记录石子异或和
int sum; //记录石子总数
int ans; //记录答案
signed main()
{
cin>>T;
while(T -- ){
cin>>n;
num = 0, sum = 0;
ans = 0;
//读入石子堆,计算所有石子的异或和、数量和
for(int i = 1; i <= n; i ++ ){
cin>>a[i];
num ^= a[i];
sum += a[i];
}
if(num){
//如果石子异或和不为0,先手只需要保证每次操作把石子的异或和置为0即可,所以这种情况下,先手必胜
int maxv = 0;
for(int i = 1; i <= n; i ++ ){
//找到能去除的最大石子数
int t = a[i] - (num ^ a[i]); //从a[i]中取出t颗石子可以让所有石子堆异或和为0
maxv = max(t, maxv); //记录最大的t值
}
for(int i = 1; i <= n; i ++ ){
//找第一步有几个可以的方案
int t = a[i] - (num ^ a[i]); //从a[i]中取出t颗石子可以让所有石子堆异或和为0
if(t == maxv) ans ++ ; //在某一堆石子中拿走maxv个石子,有几堆合适的就哪有几种操作数
}
cout<<sum - maxv + 1<<' '<<ans<<endl; //输出回合数和操作种类(有几个符合的石子堆就是可以从几个石子堆中拿走石子,就是有几种操作)
}
else{
//先手必败
set<int>S, tt;
//记录每堆石子被Alice拿走一个石子之后的情况
for(int i = 1; i <= n; i ++ ){
int t = a[i] ^ (a[i] - 1); //记录所有在第a[i]堆中取出一个石头后剩下的石子堆异或和的情况
S.insert(t); //记录在集合中
}
for(auto x : S){
//遍历所有 每堆石子被Alice拿走一个石子之后的情况
for(int i = 1; i <= n; i ++ ){
//对于第i堆石子
//下一步就是把a[i]变为a[i]^x
int t = a[i] ^ x; //在第a[i]堆中取出1个石头 此时sum=x,我们想要sum重新变为0,需要让a[i]变成a[i] ^ x,这样才能抵消x
if(a[i] - t > 1){
//这一步就是对于a[i],需要拿走多少块可以让所有石子异或和为0,如果大于1,这个方案应该去除
tt.insert(x); // 如果剩余个数不为1,那么这个x需要删除,说明这种情况不行,后续要去除
}
}
}
for(auto op : tt) S.erase(op); //遍历去除不合适的情况
//第一轮可能的操作数就是取1后下一步必须取1的堆的数量
for(int i = 1; i <= n; ++ i) if(S.count(a[i] ^ (a[i] - 1))) ans ++ ;
cout<<sum<<' '<<ans<<endl;
}
}
return 0;
}
D Link with Game Glitch