假期计划 \operatorname{假期计划} 假期计划
题目链接: j z o j 3936 jzoj\ 3936 jzoj 3936
题目
航空公司开设了连接着 N N N 个城市的航班。像任何航线一样,这些城市中的 K K K 个被设为枢纽。
现在,航空公司提供 M M M 个单行航班,其中航班 i i i 从城市 u i u_i ui 到城市 v i v_i vi 并花费 d i d_i di 美元。像任何明智的航线一样,对于每一个航班, u i u_i ui 和 v i v_i vi 中至少一个是 枢纽。两个城市间最多有一个直飞航班,并且没有航班起点与终点为同一城市。
小 X 负责为航空公司运营票务,他收到了 Q Q Q 个学生假期的单行航班的请求, 其中第 i i i 个请求是从城市 a i a_i ai 到另一个城市 b i b_i bi 。
由于小 X 被处理这些票的工作淹没,请帮他计算每个购票请求能否被实现, 以及如果能实现时它的最小花费。
为了减小输出大小,你只要输出可能的购票请求的总数,以及它们总花费的 最小值。
输入
第
1
1
1 行:
N
,
M
,
K
N,M,K
N,M,K 和
Q
Q
Q 。
第
2..1
+
M
2..1+M
2..1+M 行:第
i
+
1
i+1
i+1 行包含
u
i
,
v
i
u_i,v_i
ui,vi 和
d
i
d_i
di 。
第
2
+
M
.
.
1
+
M
+
K
2+M..1+M+K
2+M..1+M+K 行:每行包含一个中枢的编号。
第
M
+
K
+
2..
M
+
K
+
Q
+
1
M+K+2..M+K+Q+1
M+K+2..M+K+Q+1 行:每行两个数,表示从
a
i
a_i
ai 到
b
i
b_i
bi 的购票请求。
输出
第
1
1
1 行:能实现的购票请求数。
第
2
2
2 行:能实现的购票请求的最小总花费。
样例输入
3 3 1 2
1 2 10
2 3 10
2 1 5
2
1 3
3 1
样例输出
1
20
样例解释
对于第一个航班,唯一可行的路线是 1 − > 2 − > 3 1-\!\!>2-\!\!>3 1−>2−>3 ,花费 20 20 20 。
数据范围
对于
30
%
30\%
30% 的数据,
N
<
=
100
,
M
<
=
2
,
000
N<=100,M<=2,000
N<=100,M<=2,000 。
对于
100
%
100\%
100% 的数据,
1
<
=
N
,
M
<
=
20
,
000
,
1
<
=
K
<
=
200
,
1
<
=
Q
<
=
50
,
000
,
1
<
=
d
i
<
=
10
,
000
1<=N,M<=20,000,1<=K<=200,1<=Q<=50,000, 1<=d_i<=10,000
1<=N,M<=20,000,1<=K<=200,1<=Q<=50,000,1<=di<=10,000 。
思路
这道题是一道最短路。
我们把所有的纽扣作为起点做 spfa ,然后对于每一个询问,要么可以直接到,要么就可以有一个节点分别到两个点。
然后就把可以完成的请求数量和所有可以完成的请求的花费加起来输出。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct node {
int to, next, x;
}e[20001];
int n, m, k, q, x, y, z, le[20001], kk, f[201][20001], point[20001], temp, ans1, ans2;
bool in[20001];
void add(int x, int y, int z) {
e[++kk] = (node){y, le[x], z}; le[x] = kk;
}
void spfa(int s, int num) {
queue<int>qu;
memset(f[num], 0x7f, sizeof(f[num]));
temp = f[num][0];
f[num][s] = 0;
qu.push(s);
in[s] = 1;
while (!qu.empty()) {
int now = qu.front();
qu.pop();
for (int i = le[now]; i; i = e[i].next)
if (f[num][now] + e[i].x < f[num][e[i].to]) {
f[num][e[i].to] = f[num][now] + e[i].x;
if (!in[e[i].to]) {
in[e[i].to] = 1;
qu.push(e[i].to);
}
}
in[now] = 0;
}
}
int main() {
scanf("%d %d %d %d", &n, &m, &k, &q);//输入
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &x, &y, &z);//输入
add(x, y, z);//连边
}
for (int i = 1; i <= k; i++) {
scanf("%d", &x);//读入
point[x] = i;//记录纽扣
spfa(x, i);//求出此纽扣到所有点的距离
}
for (int i = 1; i <= q; i++) {
scanf("%d %d", &x, &y);//读入
if (point[x] && f[point[x]][y] != temp) {//能直接到
ans1++;
ans2 += f[point[x]][y];
}
else if (!point[x]) {//这个点不是纽扣
int an = temp;
for (int j = le[x]; j; j = e[j].next)
if (f[point[e[j].to]][y] != temp)//从一个纽扣可以分别到两个点
an = min(an, e[j].x + f[point[e[j].to]][y]);//找出最少的哪种方法
if (an != temp) {//可以到
ans1++;
ans2 += an;
}
}
}
printf("%d\n%d", ans1, ans2);//输出
return 0;
}