Codeforces 1601B. Frog Traveler


传送门

题目大意

一只蛙,在地面以下 \(n(1\leq n\leq 3\times10^5)\) 米处,要向上爬到地面,在地面以下 \(i\) 米处蛙可以向上爬 \([0,a_i](0\leq a_i\leq i)\) 米,每爬完一次,蛙会休息一次,在地面以下 \(i\) 米处休息会滑落 \(b_i(0\leq a_i\leq n - i)\) 米。求蛙爬到地面需要的最少次数,并且输出方案,即每次攀爬(休息之前)所到达的高度。

思路

考虑直接 \(bfs\) ,记 \(f_v\) 为攀爬一次并且休息滑落后到达 \(v\) 需要的最小攀爬次数,从 \(v\) 我们显然可以爬到 \(u\in[v-a[v],v]\) ,然后滑落到 \(u+b_u\) 。此时如果 \(f_{u+b_u}\) 尚为更新,则 \(f_{u+b_u}=f_v+1\) ,我们用一个 \(set\) 来维护二元组 \((u,u+b_u)\) ,其中记录所有 \(f_{u+b_u}\) 尚为更新的节点,每次从 \(v\) 转移时,遍历 \(set\)\(u\in[v-a[v],v]\) 的二元组,然后更新完 \(f_{u+b_u}\) 后,将 \(set\) 中所有第二维为 \(u+b_u\) 的元素删去,表示这些元组以后不会被更新,即转移时不会被遍历到,最后 \(f_0\) 即为答案。这样每个二元组至多被访问,删除 \(1\) 次,因此复杂度为 \(O(nlogn)\) 。对于输出方案,直接在 \(bfs\) 时记录一下前驱即可,注意因为要输出休息前的高度,而 \(bfs\) 中是休息后的高度,做一些修改即可。

代码

#include
#include
#include
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair;
using TP = tuple;
#define all(x) x.begin(),x.end()
#define pb push_back
#define mk make_pair
//#define int LL
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
//#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 300010;

int N, A[maxn], B[maxn], f[maxn], pre[maxn], fa[maxn];
vectorU[maxn], ans;
sets;

void solve()
{
	s.insert(PII(0, 0));
	for (int i = 1; i <= N; i++)
		s.insert(PII(i, i + B[i]));
	queueque;
	que.push(N);
	f[N] = 0, pre[N] = -1;
	for (auto& a : U[N])
	{
		auto  it2 = s.find(PII(a, N));
		if (it2 == s.end())
			break;
		s.erase(*it2);
	}
	while (!que.empty())
	{
		int v = que.front();
		que.pop();
		while (true)
		{
			auto it = s.lower_bound(PII(v - A[v], 0));
			if (it == s.end() || it->first > v)
				break;
			int k = it->second;
			f[k] = f[v] + 1;
			pre[k] = it->first;
			fa[it->first] = pre[v];
			que.push(k);
			for (auto& a : U[k])
			{
				auto it2 = s.find(PII(a, k));
				if (it2 == s.end())
					break;
				s.erase(*it2);
			}
		}
	}
	cout << (f[0] == inf ? -1 : f[0]) << endl;
	if (f[0] != inf)
	{
		int now = 0;
		while (now != -1)
		{
			ans.push_back(now);
			now = fa[now];
		}
		for (int i = f[0] - 1; i >= 0; i--)
			cout << ans[i] << ' ';
		cout << endl;
	}
}

int main()
{
	IOS;
	cin >> N;
	for (int i = 0; i <= N; i++)
		f[i] = inf;
	for (int i = 1; i <= N; i++)
		cin >> A[i];
	for (int i = 1; i <= N; i++)
		cin >> B[i], U[B[i] + i].pb(i);
	U[0].pb(0);
	solve();

	return 0;
}