HDU 6166 Senior Pan
首先考虑到把集合拆成两部分然后代进去跑多源多汇最短路,这样子求出来的最小值就是答案。
然后考虑怎么拆分集合能够计算到所有的答案,其实对于规模为$n$的点拆分$log$次就够了,因为两个点的编号的二进制表示一定有一位是不同的,每一次选取所有该位为$1$的点作源,所有该位为$0$的点作汇,然后跑一跑最短路就好了。
事实上随机化拆分也是可以过的233。
时间复杂度$O(nlog^2n)$。
Code:
#include <cstdio> #include <cstring> #include <queue> #include <iostream> using namespace std; typedef long long ll; typedef pair <ll, int> pin; const int N = 1e5 + 5; const ll inf = 0x3f3f3f3f3f3f3f3f; int testCase, n, m, K, tot = 0, head[N], a[N]; ll dis[N]; bool vis[N], mark[N]; struct Edge { int to, nxt; ll val; } e[N << 2]; inline void add(int from, int to, ll val) { e[++tot].to = to; e[tot].val = val; e[tot].nxt = head[from]; head[from] = tot; } template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } template <typename T> inline void chkMin(T &x, T y) { if(y < x) x = y; } priority_queue <pin> Q; void dij() { memset(vis, 0, sizeof(vis)); for(; !Q.empty(); ) { int x = Q.top().second; Q.pop(); if(vis[x]) continue; vis[x] = 1; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(dis[y] > dis[x] + e[i].val) { dis[y] = dis[x] + e[i].val; Q.push(pin(-dis[y], y)); } } } } int main() { read(testCase); for(int Case = 1; Case <= testCase; ++Case) { read(n), read(m); tot = 0; memset(head, 0, sizeof(head)); for(int i = 1; i <= m; i++) { int x, y; ll v; read(x), read(y), read(v); add(x, y, v); } read(K); for(int i = 1; i <= K; i++) read(a[i]); ll ans = inf; for(int i = 0; i <= 18; i++) { memset(mark, 0, sizeof(mark)); memset(dis, 0x3f, sizeof(dis)); for(int j = 1; j <= K; j++) { if((a[j] >> i) & 1) Q.push(pin(dis[a[j]] = 0LL, a[j])); else mark[j] = 1; } dij(); for(int j = 1; j <= K; j++) if(mark[j]) chkMin(ans, dis[a[j]]); memset(mark, 0, sizeof(mark)); memset(dis, 0x3f, sizeof(dis)); for(int j = 1; j <= K; j++) { if((a[j] >> i) & 1) mark[j] = 1; else Q.push(pin(dis[a[j]] = 0LL, a[j])); } dij(); for(int j = 1; j <= K; j++) if(mark[j]) chkMin(ans, dis[a[j]]); } printf("Case #%d: %lld\n", Case, ans); } return 0; }