HDU 5521 题解
题意:给出n个点,John需要和一个住在第n个点的人在某个点碰面,再给出m个集合,每个集合中包含Si个点,这些点两两之间可以以ti时间互达,求两人需花费的最小时间,若无法走到第n个点,则输出Evil John.
2<=N<=100000;1<=ti<=1e9;Si>0;∑Si<=1e6;
共1~6组数据,6000MS
算法/思路:
第一次见到这种套路的话还是蛮有趣的,对每个集合中两两点建边显然会被轻松TLE.
使用最短路+虚拟节点:于是我们对每个集合建立一个虚拟节点,在每个集合中的节点与虚拟节点间连一条权值为t的边(最终答案要除以2),然后用spfa或者堆优化dijkstra,分别从1与n为起点求一次最短路,对每个点取最大值,最后枚举点取最小值。时间复杂度O((m+n)log(m+n)),由上述描述,0<=m<=1e6.
#include<cstring> #include<cstdio> #include<vector> #include<queue> #include<algorithm> using namespace std; int t,n,m,time,s,src; const int maxn=210000; vector<pair<int,int>> g[maxn]; int ss; const long long inf=3e14; long long ans[maxn]; long long dist[maxn]; int jl[maxn]; int tot; bool v[maxn]; priority_queue <pair<long long,int>,vector<pair<long long,int> >,greater<pair<long long,int> > > dui; void dijkstra(){ for (int i=1;i<=n+m;++i) dist[i]=inf; dist[src]=0; pair<long long,int> now; dui.push(make_pair(0,src)); memset(v,false,sizeof(v)); for (int i=1;i<=n+m;++i){ if (dui.empty()) break; now=dui.top();dui.pop(); while (v[now.second] && !dui.empty()) {now=dui.top();dui.pop();} if (v[now.second]) break; v[now.second]=true; for (int j=0;j<g[now.second].size();++j) if (!v[g[now.second][j].first]) if (now.first+g[now.second][j].second<dist[g[now.second][j].first]) { dist[g[now.second][j].first]=now.first+g[now.second][j].second;dui.push(make_pair(dist[g[now.second][j].first],g[now.second][j].first)); } } } int main(){ scanf("%d",&t);bool tmp; for (int q=1;q<=t;++q){ for (int i=1;i<=maxn;++i) g[i].clear(); scanf("%d%d",&n,&m); for (int i=1;i<=m;++i){ scanf("%d%d",&time,&s); for (int j=1;j<=s;++j) {scanf("%d",&ss);g[i+n].push_back(make_pair(ss,time));g[ss].push_back(make_pair(i+n,time));} } long long pans=inf; while(dui.size()) dui.pop(); src=1;dijkstra();for (int i=1;i<=n;++i) ans[i]=dist[i]; while(dui.size()) dui.pop(); src=n;dijkstra();for (int i=1;i<=n;++i) ans[i]=max(dist[i],ans[i]); for (int i=1;i<=n;++i) if (pans==inf||ans[i]<pans ) { pans=ans[i];tot=1;jl[tot]=i; } else if(ans[i]==pans) jl[++tot]=i; printf("Case #%d: ",q); if (pans==inf) printf("Evil John\n"); else { printf("%lld\n",pans/2); for (int i=1;i<=tot-1;++i) printf("%d ",jl[i]); printf("%d\n",jl[tot]); } } return 0; }