1. 程式人生 > 實用技巧 >「二進位制分組」HDU 6166 Senior Pan

「二進位制分組」HDU 6166 Senior Pan

簡述

原題面: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;
}