C. The Ancient Wizards' Capes
题目描述
有 个巫师排成一排,从左到右编号为 1 到 。每个巫师都有一件隐形斗篷,可以穿在左侧或右侧。
哈利从巫师 1 的位置走到巫师 的位置()。在每个巫师的位置 ,他会记录他能看到的巫师总数。
一个位于位置 的巫师能从位置 被看到,需要满足以下条件之一:
- 巫师 将斗篷穿在左侧,且 。
- 巫师 将斗篷穿在右侧,且 。
特别注意,巫师 总是能从自己的位置 看到自己。
你破译了哈利的一份陈旧清单,它是一个包含 个元素的数组 。其中第 个元素 ()是哈利从位置 看到的巫师数量。
你的任务是,在所有可能的斗篷安排中,找出有多少种安排与清单 中的数据一致。
解题思路
这个问题的核心在于理解相邻位置 和 处可见巫师数量( 和 )之间的关系。这种关系将揭示巫师 和巫师 斗篷的相对朝向。
我们用 表示巫师 把斗篷穿在左边,用 表示他穿在右边。
根据题目定义,从位置 可见的巫师数量 是以下两项之和:
- 所有位置 且斗篷在左侧的巫师。
- 所有位置 且斗篷在右侧的巫师。
这可以表示为公式:
现在,我们为位置 写出相同的公式:
让我们计算两者之差,:
第一部分简化为 。第二部分简化为 。 所以,它们的关系是:
整理后得到:
这个方程告诉我们,两个相邻巫师( 和 )的斗篷朝向之和是由他们的可见巫师数之差()决定的。让我们分析 的几种可能情况:
- : 。由于 只能是 0 或 1,这意味着 且 。两个巫师都把斗篷穿在左边。
- : 。这意味着 且 。两个巫师都把斗篷穿在右边。
- : 。这意味着一个巫师的斗篷在左边 (1),另一个在右边 (0)。他们的斗篷朝向相反。
这个发现是关键:斗篷 相对于斗篷 的朝向是唯一确定的。如果我们知道了第一个巫师斗篷的朝向,我们就可以逐个确定后续所有巫师的朝向。
这使得整排巫师只剩下两种可能的有效安排:
- 一种是巫师 1 的斗篷在左边开始的安排。
- 另一种是巫师 1 的斗篷在右边开始的安排。
我们的算法如下:
- 测试第一种可能性: 假设巫师 1 的斗篷在左边。
- 从 迭代到 ,利用上面推导的关系,根据 、 和已知的巫师 的斗篷朝向,来确定巫师 的斗篷朝向。
- 在确定了完整的安排后,我们必须对其进行验证。根据这个安排从头计算可见性数组
vis,并检查是否对所有 都满足vis[i] == a[i]。 - 如果该安排有效,我们将其计为一个解。
- 测试第二种可能性: 重复此过程,这次假设巫师 1 的斗篷在右边。
- 如果第二种安排也有效,我们将其加入计数。
- 最终答案是总计数,可能是 0、1 或 2。
查看代码
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <map>
#include <numeric>
#include <set>
#include <tuple>
#include <vector>
using namespace std;
#define int long long
// #define mod 1000000007
#define N 500005
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<bool> a_side(n, false); // false; visibile from left
for (int i = 1; i < n; i++) {
if (a[i] == a[i - 1]) {
a_side[i] = !a_side[i - 1];
} else {
a_side[i] = a_side[i - 1];
}
}
auto func_verify = [&](vector<int> &a, vector<bool> &a_side) -> bool {
int vis_t = 0;
vector<int> vis(n, 0);
for (int i = 0; i < n; i++) {
vis[i] += vis_t;
vis_t += a_side[i] == false ? 1 : 0;
}
vis_t = 0;
for (int i = n - 1; i >= 0; i--) {
vis[i] += vis_t;
vis_t += a_side[i] == true ? 1 : 0;
}
// if vis == a, return true
for (int i = 0; i < n; i++) {
if (vis[i]+1 != a[i]) {
return false;
}
}
return true;
};
int ans = func_verify(a, a_side) ? 1 : 0;
a_side[0] = true;
for (int i = 1; i < n; i++) {
if (a[i] == a[i - 1]) {
a_side[i] = !a_side[i - 1];
} else {
a_side[i] = a_side[i - 1];
}
}
ans += func_verify(a, a_side) ? 1 : 0;
cout << ans << endl;
}
int32_t main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
#ifdef DEBUG
freopen("./input.txt", "r", stdin);
#endif
int t;
cin >> t;
while (t--) {
#ifdef DEBUG
static int test_num = 1;
cout << "test case: " << test_num++ << endl;
#endif
solve();
}
}