P1119 灾后重建

传送门

思路:

  看到村庄的数目 ≤ 200,很显然可以用 floyd 来做。

  但如果对于每次询问时间 t 都暴力地跑一遍 floyd ,时间复杂度为 n4 ,只能拿到 50 分。

  所以,要考虑一下如何优化 。。。

  因为每个村庄的 t [ i ],及询问 ( x , y , t ) 中的 t 都是不下降的,所以可以从第一个村庄 0 开始找( k = 0 ),如果 t [ k ] > 这次询问的 t ,就相当于把这之前的任意两个村庄的最短路处理好了(或者是两个村庄不连通),直接输出 d[ x ][ y ]。不然,就 k++ ,继续把村庄 k 作为中转点,跑 floyd ,直 t [ k ] > 这次询问的 t 。( 因为所有的 t 都是有序的,所以 k 不用再重新赋为 0 ;如果时间 t 是无序的,也可以离线处理,sort 一遍,再求最短路。)

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<stack>
#include<vector>
#include<queue>
#include<deque>
#include<map>
#include<set>
using namespace std;
#define lck_max(a,b) ((a)>(b)?(a):(b))
#define lck_min(a,b) ((a)<(b)?(a):(b))
#define INF 0x3f3f3f3f
typedef long long LL;
const int maxn=5e4+7;
LL n,m,d[205][205],t[maxn];
inline LL read()
{
    LL kr=1,xs=0;
    char ls;
    ls=getchar();
    while(!isdigit(ls))
    {
        if(!(ls^45))
            kr=-1;
        ls=getchar();
    }
    while(isdigit(ls))
    {
        xs=(xs<<1)+(xs<<3)+(ls^48);
        ls=getchar();
    }
    return xs*kr;
}
inline void out(LL xs)
{
    if(!xs) {putchar(48); return;}
    if(xs<0) putchar('-'),xs=-xs;
    int kr[57],ls=0;
    while(xs) kr[++ls]=xs%10,xs/=10;
    while(ls) putchar(kr[ls]+48),ls--;
}
inline void clear()
{
    memset(d,INF,sizeof(d));
    memset(t,INF,sizeof(t));
}
LL x,y,z,q,k;
int main()
{
    n=read();m=read();
    clear();
    for(LL i=0;i<n;i++) t[i]=read(),d[i][i]=0; 
    for(LL i=1;i<=m;i++)
    {
        x=read();y=read();z=read();d[x][y]=d[y][x]=z;
    }
    q=read();
    while(q--)
    {
        x=read();y=read();z=read();
        while(t[k]<=z)
        {
            for(LL i=0;i<n;i++)
                for(LL j=0;j<n;j++)
                    d[i][j]=lck_min(d[i][j],d[i][k]+d[k][j]);
            k++;
        }
        if(d[x][y]==INF||t[x]>z||t[y]>z) out(-1),putchar('\n');
        else out(d[x][y]),putchar('\n');
    }
return 0;
}

猜你喜欢

转载自www.cnblogs.com/lck-lck/p/9850156.html