标签:following one 集合 str ems span led 代码 sea
Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1358 Accepted Submission(s): 435
题目链接:HDU 5521
题意:给定点数n和集合个数m,然后给你m个集合,每一个集合有si个点,两两之间的到达时间都是ti,一个人在1,一个人在n,求两人同时出发的相遇的最短时间
由于每一个集合的点有很多,若集合两两之间连边,边数非常大,一开始这样就超时了……然后正确做法是对每一个集合再虚拟一个节点(范围是[n+1,n+m]),给每一个集合内的点连边权为ti的双向边到本集合对应虚拟节点,集合内其他点不连边,这样就可以通过虚拟的节点来到达其他地方从而减少边数(方法真是太巧妙了),然后求相遇的最短时间,显然现在无法得知到底选哪个地点作为见面地点,那就对1跑一遍单源最短路,对n跑一边单源最短路,然后统计1~n中每一个点的可能最短时间(一个人早到一个人晚到,显然用max取时间长的那个数),然后选出1~n中的最短时间mndx,再遍历一下看哪些点的最短时间为mndx并记录输出,最后记得把最短时间除以2,因为连的边是ti,进入虚拟节点又出来会多算一次
点数为1e5,题目中说了所有集合大小之和不会超过1e6,每一个集合都有2*|Si|条边,那就是2*1e6条边因此N可设为1e5+10,M可设为2*1e6+10。
以上是以前的解法,昨天计蒜客被惨虐之后仔细看了一下D题发现其实跟这道题是同一个原理,这题是群内的点之间两两距离为ti,那不妨把Block点拆成入口和出口,然后这样连边:
<u, Block入口, 0>,<Block出口, u , 0>,<Block入口, Block出口, ti>
然后从S和T各跑一遍SPFA然后记录下Max更新答案即可,也不用像上面的解法一样除以2了,点数最差情况下一个点作为Block,应该是3e5,边数应该为2sum{Si}+m,应该是2e6+1e5
代码:
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define LC(x) (x<<1) #define RC(x) ((x<<1)+1) #define MID(x,y) ((x+y)>>1) #define CLR(arr,val) memset(arr,val,sizeof(arr)) #define FAST_IO ios::sync_with_stdio(false);cin.tie(0); typedef pair<int, int> pii; typedef long long LL; const double PI = acos(-1.0); const int N = 3e5 + 7; const int M = 2e6 + 1e5 + 7; struct edge { int to, nxt; LL w; edge() {} edge(int _to, int _nxt, LL _w): to(_to), nxt(_nxt), w(_w) {} }; edge E[M]; int head[N], tot; int vis[N]; LL ds[N], de[N], Mindist[N]; void init() { CLR(head, -1); tot = 0; } inline void add(int s, int t, LL d) { E[tot] = edge(t, head[s], d); head[s] = tot++; } void spfa(int s, int flag, LL d[]) { CLR(vis, 0); if (flag) CLR(ds, INF), ds[s] = 0; else CLR(de, INF), de[s] = 0; queue<int>Q; Q.push(s); while (!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = 0; for (int i = head[u]; ~i; i = E[i].nxt) { int v = E[i].to; if (d[v] > d[u] + E[i].w) { d[v] = d[u] + E[i].w; if (!vis[v]) { vis[v] = 1; Q.push(v); } } } } } int main(void) { int tcase, n, m, si, u, i; scanf("%d", &tcase); for (int q = 1; q <= tcase; ++q) { init(); scanf("%d%d", &n, &m); LL ti; for (i = 1; i <= m; ++i) { scanf("%I64d%d", &ti, &si); add(i, i + m, ti); //m while (si--) { scanf("%d", &u); add(u + (m << 1), i, 0LL); //si add(i + m, u + (m << 1), 0LL); //si } } spfa(1 + (m << 1), 1, ds); spfa(n + (m << 1), 0, de); LL ans = 0x3f3f3f3f3f3f3f3f; printf("Case #%d: ", q); vector<int>pos; for (i = 2 * m + 1; i <= 2 * m + n; ++i) { Mindist[i] = max<LL>(ds[i], de[i]); if (Mindist[i] < ans) ans = Mindist[i]; } if (ans == 0x3f3f3f3f3f3f3f3f) puts("Evil John"); else { printf("%I64d\n", ans); for (i = 2 * m + 1; i <= 2 * m + n; ++i) if (Mindist[i] == ans) pos.push_back(i - (m << 1)); int sz = pos.size(); for (i = 0; i < sz; ++i) printf("%d%c", pos[i], " \n"[i == sz - 1]); } } return 0; }
标签:following one 集合 str ems span led 代码 sea
原文地址:http://www.cnblogs.com/Blackops/p/5766284.html