【Wannafly挑战赛2 】B - Travel

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

精灵王国有N座美丽的城市,它们以一个环形排列在Bzeroth的大陆上。其中第i座城市到第i+1座城市花费的时间为d[i]。特别地,第N座城市到第1座城市花费的时间为d[N]。这些道路都是双向的。

另外,精灵们在数千年的时间里建造了M座传送门,第i座传送门连接了城市u[i]与城市v[i],并且需要花费w[i]的时间通过(可能有两座传送门连接了同一对城市,也有可能一座传送门连接了相同的两座城市)。这些传送门都是双向的。

小S是精灵王国交通部部长,她的职责是为精灵女王设计每年的巡查路线。每次陛下会从某一个城市到达另一个城市,沿路调查每个城市的治理情况,她需要找出一条用时最短的路线。

输入描述:

第一行为2个整数N、M。
第二行为N个正整数d[i]。
接下来M行每行三个正整数u[i]、v[i]、w[i]。
第M+3行为一个正整数Q,表示需要设计路线的次数。
接下来Q行每行两个正整数x、y,表示一次从城市x到城市y的旅行。

输出描述:

Q行每行一个正整数表示该次旅行的最短时间。
示例1

输入

4 1
1 2 3 6
1 3 2
5
1 2
1 4
1 3
2 3
4 3

输出

1
5
2
2
3

备注:

1 ≤ N、Q ≤ 52501,1 ≤ M ≤ 20,1 ≤ u[i]、v[i]、x、y ≤ N,1 ≤ d[i]、w[i] ≤ 2^(30)
数据量大所以不能直接开数组存全图,但是有传送点的城市很少,所以只要所有传送点判断是不是两个城市之间最近的路线,处理后再判断询问的城市间是直接走近一些还是通过一个传送点近。
#pragma comment(linker,"/STACK:1024000000,1024000000")
#include<iostream>
#include<stdlib.h>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<cmath>
#include<cstring>
#include<string>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define LL long long
using namespace std;
inline LL read();
const int MAXN = 52501+10;

LL d[MAXN],f[105][105],n,m;
int id[MAXN],a[105];

LL dist(int x,int y)
{
    if(x>y) swap(x,y);
    LL tmp1 = d[y-1]-d[x-1];
    LL tmp2 = d[n] - tmp1;
    return min(tmp1,tmp2);
}

int main(void)
{
    n = read(),m = read();
    LL num = 0,u,v,w;
    for(int i = 1; i <= n; ++i)
        d[i] = read();
    for(int i = 2; i <= n; ++i)
        d[i] += d[i-1];
    memset(f,INF,sizeof f);
    for(int i = 1; i <= m; ++i)
    {
        u = read(); v = read(); w = read();
        if(!id[u])
        {
            id[u] = ++num;
            a[num] = u;
        }
        if(!id[v])
        {
            id[v] = ++num;
            a[num] = v;
        }
        f[id[u]][id[v]] = min(f[id[u]][id[v]],w*1LL);
        f[id[v]][id[u]] = min(f[id[v]][id[u]],w*1LL);//可能有两座传送门连接了同一对城市,也有可能一座传送门连接了相同的两座城市
    }
    for(int i = 1; i <= num; ++i)
    {
        for(int j = 1; j <= num; ++j)
            f[i][j] = min(f[i][j],dist(a[i],a[j]));
    }

    for(int i = 1; i <= num; ++i)
        f[i][i] = 0;

    for(int k = 1; k <= num; ++k)
        for(int i = 1; i <= num; ++i)
            for(int j = 1; j <= num; ++j)
                f[i][j] = min(f[i][j],f[i][k]+f[k][j]);

    int q = read();
    while(q--)
    {
        u = read();
        v = read();
        LL ans = dist(u,v);
        for(int i = 1; i <= num; ++i)
            for(int j = 1; j <= num; ++j)
                ans = min(ans,dist(u,a[i])+f[i][j]+dist(a[j],v));
        printf("%lld\n",ans);
    }
}

inline LL read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值