本来不想写题解的,不过意外发现这题居然是#1。。。留念下,写个题解
题意:
一个小偷从S到T,只走最短路。给出一个时间,问此时小偷可能的位置有多少个?有可能在顶点上也可能在边上。
思路:
求两次最短路,源分别是从T出发(记为dis[])和S(记为_dis[])。
点和边分开考虑:
1.对于边(u,v) 如果dis[u]+e(u,v)+_dis[v]==dis[S],说明e(u,v)是一条最短路上的边。那么如果询问的时间落在(_dis[u],_dis[v])(注意是开区间),答案就可以+1。同时需要注意,反过来如果dis[v]+e(u,v)+_dis[u]==dis[S]也成立,那么还会产生一个区间(_dis[v],_dis[u]),这两个区间必须取并集,因为是同一条边。
2.对于点就简单了,如果dis[i]+_dis[i]==dis[S],那么点i是最短路上的点。
用数组维护下区间覆盖(记为sum[]),那么每次询问答案就是sum[Q](Q<=dis[s])。
code:
#include <algorithm> #include <iostream> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <string> #include <vector> #include <queue> #include <stack> #include <cmath> #include <list> #include <set> #include <map> using namespace std; #define N 1024 #define ALL(x) x.begin(),x.end() #define CLR(x,a) memset(x,a,sizeof(x)) typedef long long ll; typedef pair<int,int> PI; const int INF = 0x3fffffff; const int MOD = 1000000007; /*-----------code------------*/ int g[N][N],dis[N],_dis[N],sum[10010]; int n,m,s,t; bool vis[N]; void dijkstra(int dis[],int s){ memset(dis,3,sizeof(_dis)); CLR(vis,0); dis[s]=0; for(int i=0;i<n;i++){ int p=-1; for(int j=1;j<=n;j++) if(!vis[j]){ if(p==-1 || dis[j]<dis[p]) p=j; } if(p==-1) break; for(int j=1;j<=n;j++){ if(g[p][j]>10000000) continue; if(dis[j]>dis[p]+g[p][j]){ dis[j]=dis[p]+g[p][j]; } } vis[p]=true; } } int solve(int T){ if(T>dis[s]) return 1; return sum[T]; } void merge(vector<PI> &v){ sort(ALL(v)); for(int i=1;i<v.size();i++){ v[i].first=max(v[i].first,v[i-1].second+1); if(v[i].first>v[i].second){ v.erase(v.begin()+i); i--; } } } void pretreat(){ for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(g[i][j]>10000000) continue; vector<PI> v; if(_dis[i]+g[i][j]+dis[j]==dis[s]){ int l=_dis[i]+1, r=_dis[j]-1; if(l<=r) v.push_back((PI){l,r}); } if(_dis[j]+g[j][i]+dis[i]==dis[s]){ int l=_dis[j]+1, r=_dis[i]-1; if(l<=r) v.push_back((PI){l,r}); } merge(v); for(int k=0;k<v.size();k++){ sum[v[k].first]++; sum[v[k].second+1]--; } } } for(int i=1;i<=n;i++) if(_dis[i]+dis[i]==dis[s]){ sum[_dis[i]]++; sum[_dis[i]+1]--; } for(int i=1;i<=dis[s];i++) sum[i]+=sum[i-1]; } int main(){ int Case=0; while(~scanf("%d%d%d%d",&n,&m,&s,&t)){ if(Case++) puts(""); CLR(g,3); CLR(sum,0); while(m--){ int x,y,c; scanf("%d%d%d",&x,&y,&c); g[x][y]=g[y][x]=c; } if(dis[s]>10000) return -1; dijkstra(dis,t); dijkstra(_dis,s); pretreat(); scanf("%d",&m); while(m--){ int T; scanf("%d",&T); printf("%d\n",solve(T)); } } return 0; }