CodeTON Round 2 (Div. 1 + Div. 2, Rated, Prizes!) F
CodeTON Round 2 (Div. 1 + Div. 2, Rated, Prizes!) F
题意:有一排n个格子,每个格子为红色或者蓝色。Alice可以选择两个格子(其中一个格子的颜色必须是红色的)涂成白色,Bob可以选择两个格子(其中一个格子的颜色必须是红色的),最后无法操作的玩家输,求赢家。
题解:分三种情况
- 红色格子的数量大于蓝色格子的数量,Alice获胜
- 红色格子的数量小于蓝色格子的数量,Bob获胜 //第一二种情况自己模拟一下
- 红色格子的数量等于蓝色格子的数量,问题等价于轮流从中选取两个格子RB或BR,涂成白色,当谁无法选取BR或RB时输。
问题可以分为一个个由RB交替出现的格子的子问题,每个子问题可以看成一个单独的游戏,求SG函数
定义sg[i] ( RB和BR 个数和为 i 时的胜态等级)(易知长度i+1的交替序列个数和为i),所以:
SG(i) = mex{SG(j-1)^SG(i-j-2)} (长度为i+1的交替序列,给j位置和j+1位置涂白,分成了两个区)
如果按定义求复杂度是(n^2),打表发现存在循环节34。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 5e5 + 10;
int sg[205];//sg[i] i为RB或BR的数量,为长度减去1
char s[N];
void init()
{
sg[0] = 0;//sg[i] i为RB或BR的数量,为长度减去1
for (int i = 1; i <= 200; i++)
{
set<int>st;
for (int j = 0; j < i; j++)//这次操作为删除j位置后两个,剩余的两个SG函数异或起来
{
int x = 0, y = 0;//两个SG函数的值
if (j >= 1)x = sg[j - 1];
if (i - j - 2 >= 1)y = sg[i - j - 2]; ///i-j+1-2-1
st.insert(x ^ y);
}
while (st.count(sg[i]))//SG函数中的MEX操作,取集合中出现过的最小的非负整数
sg[i]++;
}
}
int getSG(int n) //34为一个循环节,打表找的规律
{
while (n > 200) {
n -= 34;
}
return sg[n];
}
void solve()
{
int n;
scanf("%d", &n);
scanf("%s", s + 1);
int sumR = 0;
for (int i = 1; i <= n; i++)
if (s[i] == 'R')sumR++;
if (sumR > n - sumR) {
puts("Alice");
return;
}
else if (sumR < n - sumR) {
puts("Bob");
return;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
int j = i;
while (s[j] != s[j + 1] && j < n)
{
j++;
}
int cnt = j - i + 1;RBRBRB...或BRBRBR...的长度
ans ^= getSG(cnt - 1);
i = j;
}
if (ans)puts("Alice");
else puts("Bob");
}
int main()
{
init();//初始化
int T;
scanf("%d", &T);
while (T--)
{
solve();
}
}