Logo FiraCode 的博客

博客

CF1369F题解

...
FiraCode
2025-12-01 12:55:18
什么意思呢

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-04-25 22:06:25

题解思路:

我们有 $T$ 轮,只有最后一轮赢了才是赢了,所以前 $T - 1$ 轮,随便就可以了。

状态:

必胜态:上一轮一定要输

必败态:上一轮一定要赢

所以我们知道了 $i - 1$ 的状态,那么我们就可以求出第 $i$ 轮的状态。

那么我们只要维护一个赢得状态值和输的状态值就可以得到结果。

难点就在他们怎么计算:

------------$Win$ 数组分割线------------

$Win_{e,e} = 0$,因为 $e \ge 1$,所以不管 $e \times 2$ 或者 $e + 1$ 都是必败态。

那么我们就可以通过 $Win_{e,e}$ 求出 $Win_{e-1,e}$ 等。

若 $e$ 是奇数:

$Win_{e,e} = 0$

$Win_{e - 1, e} = 1$只能加一转换成必胜态

$Win_{e - 2 , e} = 0$因为他是一个奇数,而奇数只能转移到必胜态,因为两个奇数相加,得偶数;奇数加一,就相当于奇数加奇数。所以只会转移到必胜态,所以他就是必败态。 $$\ dots$$ $$\ dots$$ 以此类推

$s$ 与 $e$ 奇偶性相同时,就会走向一个必败态,否则就会走向一个必胜态。

若 $e$ 是偶数:

当 $s$ 是偶数,那么 $s \times 2$ 就是必败态,所以当 $\left \lfloor \frac{e}{4} \right \rfloor < s \le \left \lfloor \frac{e}{2} \right \rfloor$,$s \times 2$ 一定是必胜态。

当在 $\left \lfloor \frac{e}{4} \right \rfloor$ 这个点时,他一定是必败态,因为他乘二,就会到 $s$ ~ $\left \lfloor \frac{e}{2} \right \rfloor$,所以一定必败。

那么我们就可以递归去判断: $dfs (s , \frac{e}{4})$。

因为 $\left \lfloor \frac{e}{4} \right \rfloor$ 乘二到这个区间,而比他小的一定超不过这个区间,所以若能到必胜态区间 $\left \lfloor \frac{e}{4} \right \rfloor - 1$ ~ $\left \lfloor \frac{e}{2} \right \rfloor$ 这个区间的数,必定是必败态,当我们可以转到 $\frac{e}{2}$ 就是必胜态。

这就是 $Win$ 数组的求法。

------------$Lost$ 数组分割线------------

也可以类似的分析一下:

$\left \lfloor \frac{e}{2} \right \rfloor < s \le e $ 时,他一定是必胜态,那么对于 $\frac{e}{2}$ 一定是必败态,这就很像我们分析 $Win$ 时 $e$ 是偶数的情况,我们就可以直接让他转移到 $Win$ 的情况就可以了。

这样,Win 和 Lost 数组就都求出来了。

虽然我们前面说过要到着来,但是我们正着来就行了,那么当我们这一轮为先手,那么 Win 和 Lost 只有一个为 $1$,那么它就不能改变结果,若 Win 和 Lost 都是 $1$ 那么想输想赢都可以,这样最后的就可以知道了。

注意:$1 \le s_i,e_i \le 10^{18}$,要开 long long!

AC CODE:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
bool dfs (ll s , ll e) {
	if (e & 1) {\/\/奇偶性是否相同 
		if (s & 1) return false;
		else return true;
	}
	if (s > e \/ 2) {
		if (s & 1) return true; 
		else return false;
	}
	if (s > e \/ 4) return true;\/\/当他大于e\/4时,和偶数为必胜态区间范围,所以返回true 
	return dfs (s , e >> 2);\/\/递归 
}
bool check (ll s , ll e) { 
	if (s > e \/ 2) return true;\/\/奇数里这个区间为必胜态
	return dfs (s , e >> 1);\/\/否则返回和Win的共同部分 
}
int main() {
	int n;
	cin >> n;
	bool cur = false , win , lose;
	for (int i = 0; i < n; ++ i) {
		ll s , e;
		cin >> s >> e;
		win = dfs (s , e);\/\/Win
		lose = check (s , e);\/\/Lost
		win ^= cur;\/\/和是否先手异或一下 
		lose ^= cur;
		if (win == lose) break;\/\/若Lost和Win一样 ,break 
		if (win) cur = 1;\/\/若这一局你赢了,那么下一局你先手 
		else cur = 0;\/\/否则就是后手 
	}
	cout << win << ' ' << lose << endl; 
	return 0;
}

码字不易,求赞!

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。