灾后重建 / 村庄重建
题目链接:luogu P1119 / SSL 2036 / SSL 2643
题目大意
有一个无向图,边有权值。
然后对于某个点,它会在某个时刻之后才出现。
然后多个询问,问你在某个时间点中,两个点之间的最短距离。
如果这两个点并没有出现或者中间并没有路就输出 -1。
思路
看到点的个数只有 200 200 200,我们自然会想到用 Floyed。
但是我们可以发现,随着询问,会逐渐的有点由不存在变成存在。
那我们不能重新暴力更新最短路。
我们考虑枚举中转点的时候,因为如果没有某个中转点
x
x
x,路径就不会经过这个
x
x
x 点。(除了直接连通)
那我们就可以根据这个性质,把枚举中转点的顺序改成这个点开始存在的时间先后顺序。
那我们就按着时间处理查询(因为题目已经让时间是顺序,所以不需要离线,但是我还是离线了)。
然后按着询问的时间看是否有新的点由不存在变成存在,然后就以它为中转站跑 Floyed。
然后就可以得到那个时刻的距离了。
有一点要注意的是记得判断询问的两个点在哪个时候是否存在,如果不存在,就直接是
−
1
-1
−1 了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define INF 1e9
using namespace std;
struct quest {
int x, y, t, num, ans;
}q[50001];
int n, m, t[201], x, y, w, ckno, nowcan;
int Q, dis[201][201];
int read() {
int re = 0, zf = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') zf = -zf;
c = getchar();
}
while (c >= '0' && c <= '9') {
re = (re << 3) + (re << 1) + c - '0';
c = getchar();
}
return re * zf;
}
void write(int now) {
if (now < 0) {
printf("-");
write(-now);
return ;
}
if (now > 9) write(now / 10);
putchar(now % 10 + '0');
}
bool cmp1(quest x, quest y) {
return x.t < y.t;
}
bool cmp2(quest x, quest y) {
return x.num < y.num;
}
void insert(int now) {//以新加进的点为中转站跑 Floyed
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], dis[i][now] + dis[now][j]);
}
void work() {
for (int i = 1; i <= Q; i++) {
if (q[i].t > q[i - 1].t || i == 1) {
while (nowcan < n && t[nowcan + 1] <= q[i].t) {
insert(nowcan + 1);//新加进一个点
dis[nowcan + 1][nowcan + 1] = 0;
nowcan++;
}
}
if (dis[q[i].x][q[i].y] == ckno || t[q[i].x] > q[i].t || t[q[i].y] > q[i].t) q[i].ans = -1;
else q[i].ans = dis[q[i].x][q[i].y];
}
}
int main() {
// freopen("rebuild.in", "r", stdin);
// freopen("rebuild.out", "w", stdout);
for (int i = 0; i <= 200; i++)//初始化
for (int j = 0; j <= 200; j++)
dis[i][j] = INF;
ckno = INF;
n = read();
m = read();
for (int i = 1; i <= n; i++) t[i] = read();
for (int i = 1; i <= m; i++) {
x = read();
y = read();
w = read();
x++;
y++;
dis[x][y] = dis[y][x] = w;
}
Q = read();
for (int i = 1; i <= Q; i++) {
q[i].x = read();
q[i].y = read();
q[i].t = read();
q[i].x++;
q[i].y++;
q[i].num = i;
}
sort(q + 1, q + Q + 1, cmp1);//其实读入已经是排好序了,所以可以不用离线
work();
sort(q + 1, q + Q + 1, cmp2);
for (int i = 1; i <= Q; i++) {
write(q[i].ans);
putchar('\n');
}
fclose(stdin);
fclose(stdout);
return 0;
}