传送门
思路:
看到村庄的数目 ≤ 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(' '); else out(d[x][y]),putchar(' '); } return 0; }