Address
https://www.lydsy.com/JudgeOnline/problem.php?id=4070
Solution
我们知道,有一个经典的建图优化:
在最短路等模型中,如果一个
N
N
个点的图要对于所有的 之间连边,边权为
|i−j|
|
i
−
j
|
,那么只需要将所有的
i<N
i
<
N
连双向边
(i,i+1)
(
i
,
i
+
1
)
,边权为
1
1
即可。
这道题里我们也可以用这样的思想:
对于所有的 ,连有向边
<Bi,j>
<
B
i
,
j
>
<script type="math/tex" id="MathJax-Element-5294">
</script> ,边权
|Bi−j|Pi
|
B
i
−
j
|
P
i
。
但这样显然是不行的,为什么呢?
(1)
复杂度不对(废话)
(2)举一例:一只 doge 在
0
0
位置,跳跃能力为 ,另一只 doge 在
12
12
位置,跳跃能力为
2
2
,那么如果按照上面的思想建图,那么 位置的 doge 跳到
6
6
之后 位置的 doge 就能收到信息,这样显然是步星的。
于是我们要想办法
把每只 doge 的路线独立起来。
取一个参数
S
S
,新建 个辅助点,分为
S
S
组,第 组表示跳跃能力为
i
i
。
对于第 组,有一对
1≤j,k≤N,j+i=k
1
≤
j
,
k
≤
N
,
j
+
i
=
k
则在第
i
i
组建双向边 ,权值
1
1
;如果 号点初始有 doge 存在,那么连单向边从第
i
i
组的第 个点到
原图第
j
j
个点,权值 。
如果跳跃能力大于
S
S
则暴力建边。
否则由原图中的 向
第
Pi
P
i
组点内的
Bi
B
i
连权值为
0
0
的有向边。
建图完毕,由 开始跑单源最短路,到
B1
B
1
的最短路为答案。
点数
O(NS)
O
(
N
S
)
,边数
O(N(S+NS))
O
(
N
(
S
+
N
S
)
)
(设
N
N
和 同阶)
S
S
取 时复杂度比较理想。
Code
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Step(i, a, b, x) for (i = a; i <= b; i += x)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 3e4 + 5, M = 303e4 + 5, L = 15e6 + 5, INF = 0x3f3f3f3f;
int n, m, B[N], P[N], ecnt, nxt[L], adj[M], go[L], val[L], S, dis[M];
bool vis[M];
queue<int> Q;
void add_edge(int u, int v, int w)
{
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
}
int which(int x, int y)
{
return x * n + y;
}
int SSSP()
{
memset(dis, INF, sizeof(dis));
dis[B[1]] = 0; Q.push(B[1]);
while (!Q.empty())
{
int u = Q.front(); Q.pop(); vis[u] = 0;
Edge(u)
if (dis[u] + val[e] < dis[v])
{
dis[v] = dis[u] + val[e];
if (!vis[v]) vis[v] = 1, Q.push(v);
}
}
return dis[B[2]];
}
int main()
{
int i, j, ans;
n = read(); m = read();
For (i, 1, m) B[i] = read() + 1, P[i] = read();
S = min((int) sqrt(n), 100);
For (i, 1, S) For (j, i + 1, n)
{
add_edge(which(i, j - i), which(i, j), 1);
add_edge(which(i, j), which(i, j - i), 1);
}
For (i, 1, S) For (j, 1, n)
add_edge(which(i, j), j, 0);
For (i, 1, m)
if (P[i] <= S)
add_edge(B[i], which(P[i], B[i]), 0);
else Step(j, (B[i] - 1) % P[i] + 1, n, P[i])
if (j != B[i])
add_edge(B[i], j, abs(B[i] - j) / P[i]);
if ((ans = SSSP()) == INF) puts("-1");
else cout << ans << endl;
return 0;
}