2015 ACM/ICPC 沈阳区域赛 现场赛 M—Meeting 【Dijkstra】
http://acm.hdu.edu.cn/showproblem.php?pid=5521
题意:
给你n(n<=1e5)个点,m个关系:每个关系代表一个集合,包含权值v,表示该集合集合中两两的距离,还有集合的点的个数cnt和集合里的点的标号,相当于一个有距离的完全图。
保证集合中所有的点的数量不超过1e6。
两个人分别从1点和n点走,只可以在一个点相遇(一个人可以等另一个人),求最短相遇时间和最短时间的前提下在哪些编号的点相遇时间都是最短。
如果无法相遇,输出“Evil John”。
分析:
一看就是两个点的Dijkstra求最短路径,但是如何建图就很困难。因为一个完全图需要建n*(n-1)/2的边,容易超时,只能用组的方式建边。对于集合中每个点 向集合的中心点(n+i)建边 add(x,n+i,v);add(n+i,x,0)。保证集合中的任意两个点的距离是v。
图建的很巧妙。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
#define pa pair<int,int>
using namespace std;
const int maxn = 2200010;
struct node
{
int to,next;
ll v;
};
struct Dijkstra
{
node edge[maxn];
int cnt,head[maxn],n;
ll dis[maxn];
void init(int nn)
{
n=nn;
cnt=0;
for(int i=0;i<=n;i++) head[i]=0;
}
void add(int u,int v,int w)
{
cnt++;
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
edge[cnt].v=w;
}
void dijkstra(int s)
{
priority_queue<pa,vector<pa>,greater<pa> >q;
int i,now;
for (i=1;i<=n;i++)
dis[i]=inf;
dis[s]=0;
q.push(make_pair(0,s));
while (!q.empty())
{
now=q.top().second;
q.pop();
for (i=head[now];i;i=edge[i].next)
if (dis[now]+edge[i].v<dis[edge[i].to])
{
dis[edge[i].to]=dis[now]+edge[i].v;
q.push(make_pair(dis[edge[i].to],edge[i].to));
}
}
}
}D1,DN;
int n,m;
ll v;
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
D1.init(n+m+1);
DN.init(n+m+1);
for(int i=0;i<m;i++)
{
int cnt;
scanf("%lld%d",&v,&cnt);
for(int j=0;j<cnt;j++)
{
int x;
scanf("%d",&x);
D1.add(x,n+i+1,0);
D1.add(n+i+1,x,v);
DN.add(x,n+i+1,0);
DN.add(n+i+1,x,v);
}
}
D1.dijkstra(1);
DN.dijkstra(n);
ll mi=inf;
for(int i=1;i<=n;i++)
{
D1.dis[i]=max(D1.dis[i],DN.dis[i]);
mi=min(mi,D1.dis[i]);
}
printf("Case #%d: ",cas++);
if(mi==inf) puts("Evil John");
else
{
printf("%d\n",mi);
bool fg=0;
for(int i=1;i<=n;i++)
if(mi==D1.dis[i]){
if(!fg) printf("%d",i),fg=1;
else printf(" %d",i);
}
puts("");
}
}
return 0;
}