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;
}
posted @ 2022-04-05 20:43  illume  阅读(60)  评论(0)    收藏  举报