ICPC沈阳站M题,对于每一个集合新建一个点,集合里面每个点到这个点的代价是ti,这个点到集合的每个点代价是0,从1和n出发跑两次最短路就行了。
#include <bits/stdc++.h>
using namespace std;
#define maxn 4111111
#define maxm 21111111
#define INF 11111111111111111
struct node {
int to, next;
long long w;
}edge[maxm];
int n, m, s, u, v, w, head[maxn], cnt;
long long d1[maxn], d2[maxn];
void add_edge (int u, int v, long long w, int i) {
node &e = edge[i];
e.to = v, e.w = w, e.next = head[u], head[u] = i;
}
bool vis[maxn];
int top, num[maxn];
bool spfa (int start, long long *d) {
memset (vis, 0, sizeof vis);
for (int i = 1; i <= n+m; i++) {
d[i] = INF;
}
vis[start] = 1;
d[start] = 0;
queue <int> q;
while (!q.empty ()) q.pop ();
q.push (start);
memset (num, 0, sizeof num);
num[start] = 1;
while (!q.empty ()) {
int u = q.front (); q.pop ();
vis[u] = 0;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (d[v]>d[u]+edge[i].w) {
d[v] = d[u]+edge[i].w;
if (!vis[v]) {
vis[v] = 1;
q.push (v);
if (++num[v] > n+m)
return 0;
}
}
}
}
return 1;
}
int ans_cnt, ans[maxn];
int main () {
//freopen ("in", "r", stdin);
int t, kase = 0;
scanf ("%d", &t);
while (t--) {
memset (head, -1, sizeof head);
cnt = 0;
scanf ("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf ("%lld%d", &w, &s);
for (int j = 1; j <= s; j++) {
scanf ("%d", &u);
add_edge (n+i, u, w, cnt++);
add_edge (u, n+i, 0, cnt++);
}
}
spfa (1, d1);
spfa (n, d2);
long long Min = INF;
ans_cnt = 0;
for (int i = 1; i <= n; i++) {
long long cur = max (d1[i], d2[i]);
if (cur < Min) {
Min = cur;
ans_cnt = 1;
ans[ans_cnt] = i;
}
else if (cur == Min) {
ans_cnt++;
ans[ans_cnt] = i;
}
}
printf ("Case #%d: ", ++kase);
if (Min == INF) {
printf ("Evil John\n");
}
else {
printf ("%lld\n", Min);
for (int i = 1; i < ans_cnt; i++)
printf ("%d ", ans[i]);
printf ("%d\n", ans[ans_cnt]);
}
}
return 0;
}