「二進位制分組」HDU 6166 Senior Pan
阿新 • • 發佈:2020-11-13
簡述
原題面:hdu
\(T\) 組資料,每次給定一張 \(n\) 個點 \(m\) 條邊的有向圖,邊有邊權。
給定 \(k\) 個關鍵點,求 \(k\) 個關鍵點兩兩距離的最小值。
\(1\le T\le 5\),\(1\le k\le n\le 10^5\),\(1\le m\le 10^5\),\(1\le\) 邊權 \(\le 10^5\)。
6S,128MB。
分析
二進位制分組 + 多源最短路。
二進位制分組多源最短路裸題。
列舉二進位制位,根據 關鍵點的編號 這一位是否為 1 進行分組,將這一位為 1 的作為起點,跑多源最短路。統計到達這一位不為 1 的關鍵點的最短路長度。
由於所有關鍵點的編號都不同,能保證兩個關鍵點在某一次分組中不在同一集合裡,因此可以統計到所有點對的情況。
由於是有向圖,注意正反各跑一遍。
使用堆優化 Dijkstra
實現,時間複雜度 \(O((n+m)\log (n+m)\log k)\)。
注意常數。
實現
//知識點:多源最短路 /* By:Luckyblock */ #include <algorithm> #include <cctype> #include <cstdio> #include <cstring> #include <queue> #define LL long long #define pr std::pair #define mp std::make_pair const int kN = 1e5 + 10; const int kM = 2e5 + 10; const LL kInf = 1e12 + 2077; //============================================================= int n, m, k, pos[kN]; int e_num, head[kN], v[kM], w[kM], ne[kM]; LL ans, dis[kN]; bool vis[kN], start[kN]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void Chkmax(int &fir_, int sec_) { if (sec_ > fir_) fir_ = sec_; } void Chkmin(LL &fir_, LL sec_) { if (sec_ < fir_) fir_ = sec_; } void Init() { ans = kInf; e_num = 0; memset(head, 0, sizeof (head)); } void AddEdge(int u_, int v_, int w_) { v[++ e_num] = v_; w[e_num] = w_; ne[e_num] = head[u_]; head[u_] = e_num; } void Dijkstra() { std::priority_queue <pr <LL, int> > q; memset(dis, 63, sizeof (dis)); memset(vis, 0, sizeof (vis)); for (int i = 1; i <= k; ++ i) { if (! start[i]) continue ; dis[pos[i]] = 0; q.push(mp(0, pos[i])); } while (! q.empty()) { int u_ = q.top().second; q.pop(); if (vis[u_]) continue ; vis[u_] = true; for (int i = head[u_]; i; i = ne[i]) { int v_ = v[i], w_ = w[i]; if (1ll * dis[u_] + w_ < 1ll * dis[v_]) { dis[v_] = 1ll * dis[u_] + 1ll * w_; q.push(mp(-dis[v_], v_)); } } } } //============================================================= int main() { int T = read(); for (int nowT = 1; nowT <= T; ++ nowT) { Init(); n = read(), m = read(); for (int i = 1; i <= m; ++ i) { int u_ = read(), v_ = read(), w_ = read(); AddEdge(u_, v_, w_); } k = read(); for (int i = 1; i <= k; ++ i) pos[i] = read(); for (int bit = 0; (1 << bit) <= k; ++ bit) { for (int j = 1; j <= k; ++ j) { start[j] = (j & (1 << bit)); } Dijkstra(); for (int j = 1; j <= k; ++ j) { if (! start[j]) Chkmin(ans, dis[pos[j]]); } for (int j = 1; j <= k; ++ j) { start[j] = !start[j]; } Dijkstra(); for (int j = 1; j <= k; ++ j) { if (! start[j]) Chkmin(ans, dis[pos[j]]); } } printf("Case #%d: %lld\n", nowT, ans); } return 0; }