LOJ#3087. 「GXOI / GZOI2019」旅行者 二進位制分組+Dijkstra
阿新 • • 發佈:2020-07-27
通過這道題了解二進位制分組.
由於我們只需要求兩兩之間最短路的值而不需要求具體是哪兩個點得到的最短路,可以使用二進位制分組.
因為如果兩個點對答案有貢獻,那麼這兩個點一定在某個二進位制位上不同,而 dijkstra 可以方便地求兩個集合之間的最短路.
然後注意對於兩個方向要分別跑一個 dijkstra.
總時間複雜度為 $O( (n+m) \log (n+m) \log n)$.
code:
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N 100006 #define M 500009 #define ll long long #define pb push_back #define setIO(s) freopen(s".in","r",stdin) using namespace std; const ll inf=1000000000000000; int n,m,K,s,t,tot; int A[N]; struct Edge { int v,c; Edge(int v=0,int c=0):v(v),c(c){} }; vector<Edge>G[N]; struct Dijkstra { ll d[N]; int vis[N]; struct data { int u;ll d; data(int u=0,ll d=0):u(u),d(d){} bool operator<(const data b) const { return d>b.d; } }; priority_queue<data>q; ll solve() { for(int i=0;i<=n+1;++i) { vis[i]=0,d[i]=inf; } d[s]=0; q.push(data(s,0)); while(!q.empty()) { data e=q.top(); q.pop(); int u=e.u; if(vis[u]) { continue; } vis[u]=1; for(int i=0;i<G[u].size();++i) { Edge p=G[u][i]; if(d[p.v]>d[u]+1ll*p.c) { d[p.v]=d[u]+1ll*p.c; q.push(data(p.v,d[p.v])); } } } return d[t]; } }D; void solve() { int x,y,z; scanf("%d%d%d",&n,&m,&K); for(int i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&z); if(x==y) { continue; } G[x].pb(Edge(y,z)); } for(int i=1;i<=K;++i) scanf("%d",&A[i]); s=0,t=n+1; ll ans=inf; for(int i=0;(1<<i)<=n;++i) { for(int j=1;j<=K;++j) { if((1<<i)&A[j]) { G[s].pb(Edge(A[j],0)); } else { G[A[j]].pb(Edge(t,0)); } } ans=min(ans,D.solve()); for(int j=1;j<=K;++j) { if((1<<i)&A[j]) { G[s].pop_back(); } else { G[A[j]].pop_back(); } } } for(int i=0;(1<<i)<=n;++i) { for(int j=1;j<=K;++j) { if((1<<i)&A[j]) { G[A[j]].pb(Edge(t,0)); } else { G[s].pb(Edge(A[j],0)); } } ans=min(ans,D.solve()); for(int j=1;j<=K;++j) { if((1<<i)&A[j]) { G[A[j]].pop_back(); } else { G[s].pop_back(); } } } printf("%lld\n",ans); for(int i=0;i<=n+1;++i) { G[i].clear(); } } int main() { // setIO("input"); int T; scanf("%d",&T); while(T--) { solve(); } return 0; }