給一個\(n\)個點,\(m\)條邊的有向圖。
給\(k\)個特殊點\(K_1,K_2,\cdot,K_k\)
求\(k\)個特殊點中兩兩最短路的最小值。
數據範圍:
c++
Floyd...好像暴力都沒得打。/kkspa
DAG!能夠拓撲搞。
然而並不會!code
思路真的很妙。
建超級起點\(S\)和超級終點\(T\)。
考慮枚舉每一個二進制位:
枚舉關鍵點:blog
這樣作爲何是對的呢?
其實就是須要證實一個問題:對於每一個特殊點對\(K_i,K_j\),他們都至少一次被分到了不一樣的集合。
那很顯然,由於特殊點互不相同,那麼至少有一個二進制位不一樣,就會被分到不一樣的集合。it
# include <bits/stdc++.h> using namespace std; const int N = 100005,inf = 1e9 + 7; int Test; int n,m,k; struct edge { int v,w; edge() {} edge(int _v,int _w) : v(_v),w(_w) {} }; vector <edge> g[N]; int K[N]; int dis[N]; bool vis[N]; int S,T; void dij(void) { priority_queue <pair<int,int>,vector <pair<int,int> > ,greater<pair<int,int> > > q; for(int i = 1; i <= n + 2; i++) dis[i] = inf,vis[i] = 0; dis[S] = 0; q.push(make_pair(0,S)); while(!q.empty()) { int x = q.top().second;q.pop(); if(vis[x]) continue; vis[x] = 1; for(int i = 0; i < (int)g[x].size(); i++) { int v = g[x][i].v; if(dis[v] > dis[x] + g[x][i].w) { dis[v] = dis[x] + g[x][i].w; q.push(make_pair(dis[v],v)); } } } // for(int i = 1; i <= n + 2; i++) printf("dis[%d] = %d\n",i,dis[i]); return; } int main(void) { scanf("%d",&Test); while(Test--) { scanf("%d%d%d",&n,&m,&k); for(int i = 1; i <= n; i++) g[i].clear(); for(int i = 1; i <= m; i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); g[x].push_back(edge(y,z)); } for(int i = 1; i <= k; i++) { scanf("%d",&K[i]); } S = n + 1, T = n + 2; int ans = inf; for(int i = 0; i == 0 || (1 << (i - 1)) <= n; i++) { g[S].clear(); vector <int> S1; for(int j = 1; j <= k; j++) { if(K[j] >> i & 1) { g[S].push_back(edge(K[j],0)); } else g[K[j]].push_back(edge(T,0)),S1.push_back(K[j]); } dij(); ans = min(ans,dis[T]); for(int j = 0; j < (int)S1.size(); j++) { // printf("del = %d\n",S1[j]); g[S1[j]].pop_back(); } S1.clear(); g[S].clear(); for(int j = 1; j <= k; j++) { if(!(K[j] >> i & 1)) { g[S].push_back(edge(K[j],0)); } else g[K[j]].push_back(edge(T,0)),S1.push_back(K[j]); } dij(); ans = min(ans,dis[T]); for(int j = 0; j < (int)S1.size(); j++) { g[S1[j]].pop_back(); } S1.clear(); } printf("%d\n",ans); } return 0; }