91 分求调(悬赏关注)
查看原帖
91 分求调(悬赏关注)
448873
Pig_py楼主2023/8/16 14:32
#include<bits/stdc++.h>
using namespace std;
int Q,n,m,k,ed;
long long ans;
long long dis[100005];
int vis[100005],h[100005],cnt;
void pre(){
    ed=0;
    cnt=0;
    
}
struct Edge{
    int frm,to;
    long long w;
}edge[500005];
struct Graph{
    int to,nxt;
    long long w;
}e[2000005];
int interest[100005];
int Expopcount(int x){
    int res=0;
    while(x){
        res++;
        x>>=1;
    }
    return res;
}
void add(int u,int v,long long w){
    cnt++;
    e[cnt].to=v;
    e[cnt].nxt=h[u];
    e[cnt].w=w;
    h[u]=cnt;
}
void dijkstra(int s){
    for(int i=1;i<=n;i++){
        dis[i]=1000000000000000ll;
        vis[i]=0;
    }
    dis[s]=0;
    priority_queue<pair<long long,int>,vector<pair<long long,int> >,greater<pair<long long,int> > >q;
    q.push(make_pair(0ll,s));
    while(!q.empty()){
        int x=q.top().second;
        q.pop();
        if(!vis[x]){
            vis[x]=1;
            for(int i=h[x];i;i=e[i].nxt){
                if(dis[e[i].to]>dis[x]+e[i].w){
                    dis[e[i].to]=dis[x]+e[i].w;
                    q.push(make_pair(dis[e[i].to],e[i].to));
                }
            }
        }
    }
}
signed main(){
    scanf("%d",&Q);
    while(Q--){
        scanf("%d%d%d",&n,&m,&k);
        pre();
        // J 国有 n 个城市,m 条道路,Vani 感兴趣的城市数量为 k
        for(int i=1;i<=m;i++){
            ed++;
            scanf("%d%d%lld",&edge[ed].frm,&edge[ed].to,&edge[ed].w);
        }
        for(int i=1;i<=k;i++){
            scanf("%d",&interest[i]);
        }
        k=unique(interest+1,interest+k+1)-interest-1;
        int bit=Expopcount(n);
        ans=1000000000000000ll;
        n+=2;
        for(int i=0;i<bit;i++){
            int zero=0,one=0;
            for(int j=1;j<=k;j++){
                if(interest[j]&(1<<i))one++;
                else zero++;
            }
            if(!one||!zero)continue;
            for(int j=1;j<=n;j++){
                h[j]=0;
            }
            cnt=0;
            for(int j=1;j<=ed;j++){
                add(edge[j].frm,edge[j].to,edge[j].w);
            }
            for(int j=1;j<=k;j++){
                if(interest[j]&(1<<i)){
                    add(interest[j],n,0);
                }
                else{
                    add(n-1,interest[j],0);
                }
            }
            dijkstra(n-1);
            ans=min(ans,dis[n]);
        }
        printf("%lld\n",ans);
    }
}
/*
这题思路很好。

我们考虑将 k 个特殊点划分到两个集合 A 和 B,将源点 S 向 A 集合所有点连边,将 B 集合
所有点向汇点 T 连边,跑 S 到 T 的最短路。

问题在于如何划分集合 A 和集合 B. 此处思路极好。我们考虑设 (x,y) 表示 k 个点中 x 和 y 
之间的路径最短。那么 x,y 至少有一个二进制位不相同。我们只需要枚举二进制的每一位,划分一下即可。
*/
2023/8/16 14:32
加载中...