题目链接
毒瘤题
卡spfa
可能dij也卡,没试过
然后放floyd过了
但其实spfa很好打,虽然只有60
让我们来分析一下spfa的效率,假设这个出题人极其毒瘤,出网格图
又因为是在线询问,所以对于每次询问都得跑一次spfa
所以效率上界为$O(n*m*Q)$
代入一下最坏情况,$200*19900*50000=199000000000$
嗯,1.99*(10^11),成功炸飞
但是spfa特别好想也特别好打
在松弛的时候多判一下$if(t[u]>T||t[v]>T)continue$就好
其他都是标准spfa操作
题目中给的t是有序的,如果是无序的需要离线处理排序一下
Code(spfa):
#include <cstdio> #include <cstring> using namespace std; #define N 80000 #define inf (1<<30) int n,m,t[N]; int cnt,head[N]; int d[N],q[N]; bool vis[N]; struct edge{int to,next,v;}e[N<<1]; void ins(int u,int v,int w){ e[++cnt].to=v;e[cnt].next=head[u];e[cnt].v=w;head[u]=cnt; e[++cnt].to=u;e[cnt].next=head[v];e[cnt].v=w;head[v]=cnt; } bool spfa(int s,int ttt,int tt){ for(int i=1;i<=n;i++)d[i]=inf; memset(vis,0,sizeof(vis)); int l=0,r=1; d[s]=0;q[0]=s;vis[s]=1; while(l<r){ int u=q[l++]; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(t[u]>tt||t[v]>tt)continue; if(d[v]>d[u]+e[i].v){ d[v]=d[u]+e[i].v; if(!vis[v])vis[v]=1,q[r++]=v; } } vis[u]=0; } if(d[ttt]==inf)return 0; return 1; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&t[i]); for(int i=1;i<=m;i++){ int x,y,w; scanf("%d%d%d",&x,&y,&w); x++;y++; ins(x,y,w); } int Q; scanf("%d",&Q); while(Q--){ int x,y,T; scanf("%d%d%d",&x,&y,&T); if(spfa(x+1,y+1,T))printf("%d ",d[y+1]); else printf("-1 "); } return 0; }
好了接下来上正解
观察到t和每个询问里面的T是有序的
所以对于每个询问的T我们只要松弛已经修好的村庄就好了
然后如果出发点/终点还没修好那么肯定也没法到,特判一下
注意要建无向边
其实floyd比spfa还好打(雾)
Code(floyd):
#include <cstdio> #include <cstring> using namespace std; #define inf 0x3f3f3f3f int f[210][210],t[40010],n,m,k; int min(int x,int y){return x<y?x:y;} int main(){ scanf("%d%d",&n,&m); memset(f,0x3f,sizeof(f)); for(int i=0;i<n;i++) scanf("%d",&t[i]),f[i][i]=0; for(int i=0;i<m;i++){ int x,y,w; scanf("%d%d%d",&x,&y,&w); f[x][y]=f[y][x]=w; } int Q; scanf("%d",&Q); while(Q--){ int x,y,w; scanf("%d%d%d",&x,&y,&w); while(t[k]<=w&&k<=n){ for(int i=0;i<n;i++) for(int j=0;j<n;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]); k++; } if(f[x][y]==inf||t[x]>w||t[y]>w)printf("-1 "); else printf("%d ",f[x][y]); } return 0; }