P5304 [GXOI/GZOI2019]旅行者(奇妙建圖+二進位制列舉)
阿新 • • 發佈:2022-04-05
題意
一個圖 \(n\) 點 \(m\) 條有向邊,裡面有 \(k\) 個特殊點,問這 \(k\) 個點之間兩兩最短路的最小值是多少?
資料範圍
\(n \leq 10^5\), \(m \leq 5 * 10 ^5\)
思路
假設我們把特殊點分成 \(A,B\) 兩個集合,新建 \(s\) 連 \(A\) 集合的所有點,邊權 \(0\) ,新建 \(t\) 連線 \(B\) 集合裡的所有點,邊權 \(0\) ,那麼 \(s\) 到 \(t\) 的最短路就是 \(A,B\) 集合點之間的最短路的最小值。
那麼對於 \(k\) 個特殊點,我們列舉二進位制裡的第 \(i\) 位,把二進位制第 \(i\)
然後跑 \(log\ n\) 次最短路之後,所有最短路的最小值就是最終答案。
原理是,假設 \(k\) 個特殊點裡最近的是 \(x\) 和 \(y\) ,那麼 \(x\) 和 \(y\) 一定有一個二進位制位不一樣,那麼他們肯定在那次分組的時候被放進了不同的集合,從而肯定被算進了最後的答案之中最短路。
code
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; //#pragma GCC optimize(3) #define pb push_back #define is insert #define PII pair<int,int> #define show(x) cerr<<#x<<" : "<<x<<endl; //mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count()); //ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);} const ll INF=0x3f3f3f3f;//2147483647; const int N=1e5+50; const ll mod=998244353; int n,m,k; vector<PII>to[N]; vector<PII>tmp[N]; int a[N]; int S=0,T; int d[N]; int vis[N]; void dijkstra(){ memset(d,INF,sizeof d); memset(vis,0,sizeof vis); d[S]=0; priority_queue<PII,vector<PII>,greater<PII>>q; q.push({d[S],S}); while(!q.empty()){ // for(int j=0;j<=n+1;j++){ // cout<<d[j]<<" "; // }cout<<endl; PII now=q.top();q.pop(); int u=now.second; // cout<<"u="<<u<<endl; if(vis[u])continue; vis[u]=1; for(auto i:to[u]){ int v=i.first,val=i.second; // cout<<v<<" "<<val<<endl; if(d[v]>d[u]+val){ d[v]=d[u]+val; if(!vis[v])q.push({d[v],v}); } } } } void solve() { cin>>n>>m>>k;T=n+1; for(int i=1;i<=m;i++){ int u,v,val;cin>>u>>v>>val; tmp[u].pb({v,val}); } for(int i=1;i<=k;i++){ cin>>a[i]; } int ans=INF; for(int i=0;i<17;i++){ for(int j=0;j<=n+1;j++){ to[j].clear(); to[j]=tmp[j]; } vector<int>A,B; for(int j=1;j<=k;j++){ if((1<<i)&a[j]){ to[S].pb({a[j],0}); A.pb(a[j]); } else { to[a[j]].pb({T,0}); B.pb(a[j]); } } dijkstra(); ans=min(ans,d[T]); } for(int i=0;i<17;i++){ for(int j=0;j<=n+1;j++){ to[j].clear(); to[j]=tmp[j]; } vector<int>A,B; for(int j=1;j<=k;j++){ if(((1<<i)&a[j])==0){ to[S].pb({a[j],0}); A.pb(a[j]); } else { to[a[j]].pb({T,0}); B.pb(a[j]); } } dijkstra(); ans=min(ans,d[T]); } cout<<ans<<"\n"; for(int i=0;i<=n+1;i++){ tmp[i].clear(); } } signed main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int __=1;cin>>__; while(__--){ solve(); } return 0; }