CF1601B Frog Traveler

题目大意

有一只青蛙掉到了井底,这口井被划分为 $n+1$ 个位置,井口是 $0$ ,井
底是 $n$ 。

现在这只青蛙想跳出这口井,假设它当前在位置 $i$,则它可以向上跳 $0$ 到 $a_i$ 的任意整数距离。

又因为井口很滑,所以如果青蛙跳到了位置 $j$,则它会往下滑 $b_j$ 个位置。

给定 $n, a, b$,你需要求出青蛙最少跳多少次才能跳出井(跳到位置 $0$ ),并给出方案。

$1 \leq n \leq 3 \times 10^5$

题目分析

如果忽略跳到位置 $i$ 会往下滑 $b_i$ 这个限制,那么这题可以轻松使用线段树优化建图+最短路解决。

考虑添加了这个限制怎么做,我们建立 $n+1$ 个虚点 $[n+1,2n+1]$ 表示跳到 $[0, n]$ 且还没往下滑时的状态。

由于 $b_i$ 不变,那么每次建边就可以拆成两个部分:

  1. $i$ 到 $[i-a[i],i]$ 的虚点,边权为 $1$。
  2. $[i-a[i],i]$ 的虚点 到其各自对应的点,边权为 $0$。

$1$ 部分可以用线段树优化建图解决, $2$ 部分则可以在初始化时建立。

最后我们发现所有边边权为 $0,1$,故我们可以用双端队列 $\texttt{bfs}$ 降低复杂度,总时间复杂度 $ \Theta (n \log n)$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <bits/stdc++.h>

#define R register
#define ll long long
#define sum(a, b, mod) (((a) + (b)) % mod)
#define meow(cat...) fprintf(stderr, cat)

const int MaxN = 4e5 + 10;
const int Max = MaxN << 5;
const int inf = 0x3f3f3f3f;

struct edge
{
int next, to, dis;
} e[Max];

int head[Max], a[MaxN], b[MaxN];
int n, m, cnt, dis[Max], vis[Max], pre[Max];

void add_edge(int u, int v, int d)
{
++cnt;
e[cnt].to = v;
e[cnt].dis = d;
e[cnt].next = head[u];
head[u] = cnt;
};

inline int read()
{
int x = 0;
char ch = getchar();
while (ch > '9' || ch < '0')
ch = getchar();
while (ch <= '9' && ch >= '0')
x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return x;
}

struct SegmentTree
{
int cnt, idx[Max];
void build(int id, int l, int r)
{
if(l == r)
return (void) (idx[id] = l + n + 1);
idx[id] = ++cnt; int mid = (l + r) >> 1;
build(id << 1, l, mid), build(id << 1 | 1, mid + 1, r);
if(idx[id << 1]) add_edge(idx[id], idx[id << 1], 0);
if(idx[id << 1 | 1]) add_edge(idx[id], idx[id << 1 | 1], 0);
}
void modify(int id, int l, int r, int ql, int qr, int u)
{
if(ql > r || l > qr) return;
if(ql <= l && r <= qr)
return add_edge(u, idx[id], 1);
int mid = (l + r) >> 1;
modify(id << 1, l, mid, ql, qr, u);
modify(id << 1 | 1, mid + 1, r, ql, qr, u);
}
} T;

void bfs()
{
std::deque<int> q;
dis[n] = 0, q.push_back(n);
while(!q.empty())
{
int u = q.front(); q.pop_front();
if(vis[u]) continue; vis[u] = 1;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(dis[v] > dis[u] + e[i].dis)
{
dis[v] = dis[u] + e[i].dis;
if(!vis[v])
{
if(e[i].dis) q.push_back(v);
else q.push_front(v); pre[v] = u;
}
}
}
}
}

signed main()
{
int now = 0;
n = read(), T.cnt = 2 * n + 1, memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++) b[i] = read();
for(int i = 0; i <= n; i++)
add_edge(i + n + 1, i + b[i], 0);
T.build(1, 0, n);
for(int i = 1; i <= n; i++)
T.modify(1, 0, n, i - a[i], i, i);
bfs(), printf("%d\n", dis[0] == inf ? -1 : dis[0]);
if(dis[0] != inf)
{
std::vector<int> path;
while(now != n)
{
if(n + 1 <= now && now <= 2 * n + 1)
path.push_back(now - n - 1);
now = pre[now];
}
std::reverse(path.begin(), path.end());
for(auto x : path) printf("%d ", x);
}
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×