【spfa】假期计划(jzoj 3936)

假期计划

jzoj 3936

题目大意

给你一个有向图( n , m ⩽ 20000 n,m\leqslant 20000 n,m20000),现在有一些作为枢纽的点,且保证每一条边的两个点至少有一个是枢纽点,现在给q个询问,问某一个点到另一个点的最短路,你只需输出可以到达的数量,和可以到达的最短路之和

输入样例

3 3 1 2
1 2 10
2 3 10
2 1 5
2
1 3
3 1

输出样例

1
20

数据范围

对于 30%的数据, N ⩽ 100 , M ⩽ 2 , 000 。 N\leqslant 100,M\leqslant 2,000。 N100M2,000
对于 100%的数据, 1 ⩽ N , M ⩽ 20 , 000 , 1 ⩽ K ⩽ 200 , 1 ⩽ Q ⩽ 50 , 000 , 1 ⩽ d i ⩽ 10 , 000 。 1\leqslant N,M\leqslant 20,000,1\leqslant K\leqslant 200,1\leqslant Q\leqslant 50,000, 1\leqslant d_i\leqslant 10,000。 1N,M20,0001K2001Q50,0001di10,000

样例说明

对于第一个航班,唯一可行的路线是 1 − > 2 − > 3 1->2->3 1>2>3,花费 20。

解题思路

因为枢纽点很少,我们对于每一个枢纽点跑一边 s p f a spfa spfa
如果询问的出发点是枢纽点,那就是直接最短路的值
如果不是,那和他相连的就都是,然后计算这条边加和他相连的点到终点的最短路就是结果

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll n, m, k, q, x, y, z, tot, num, sum, ans, p[20010], v[20010], head[20010], b[210][20010];
struct rec
{
	ll to, l, next;
}a[20010];
void spfa(ll x)
{
	memset(b[v[x]], 127/3, sizeof(b[v[x]]));
	b[v[x]][x] = 0;//以第x个枢纽为起点到各个点的最短路
	p[x] = 1;
	queue<ll>d;
	d.push(x);
	while(!d.empty())
	{
		ll h = d.front();
		d.pop();
		for (ll i = head[h]; i; i = a[i].next)
			if (b[v[x]][h] + a[i].l < b[v[x]][a[i].to])
			{
				b[v[x]][a[i].to] = b[v[x]][h] + a[i].l;
				if (!p[a[i].to])
				{
					p[a[i].to] = 1;
					d.push(a[i].to);
				}
			}
		p[h] = 0;
	}
}
int main()
{
	scanf("%lld%lld%lld%lld", &n, &m, &k, &q);
	for (ll i = 1; i <= m; ++i)
	{
		scanf("%lld%lld%lld", &x, &y, &z);
		a[++tot].to = y;
		a[tot].l = z;
		a[tot].next = head[x];
		head[x] = tot;
	}
	for (ll i = 1; i <= k; ++i)
	{
		scanf("%lld", &x);
		v[x] = i;//记录下他是第几个,方便减小内存
		spfa(x);
	}
	for (ll i = 1; i <= q; ++i)
	{
		scanf("%lld%lld", &x, &y);
		sum = 200000010;
		if (v[x]) sum = min(sum, b[v[x]][y]);//他就是枢纽
		for (ll j = head[x]; j; j = a[j].next)
			if (v[a[j].to])
				sum = min(sum, a[j].l + b[v[a[j].to]][y]);//相连的枢纽
		if (sum <= 200000000)
		{
			num++;
			ans += sum;
		}
	}
	printf("%lld\n%lld", num ,ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值