1. 程式人生 > 其它 >P5304 [GXOI/GZOI2019]旅行者(奇妙建圖+二進位制列舉)

P5304 [GXOI/GZOI2019]旅行者(奇妙建圖+二進位制列舉)

題目傳送門

題意

一個圖 \(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\)

位是 \(0\) 的點放在 \(A\)\(1\) 的點放在 \(B\) ,用以上方法跑兩個最短路(正反都需要跑)。

然後跑 \(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;
}